Skip to content

feat: export interpreter error types for errors.As classification - #215

Merged
Azorlogh merged 4 commits into
mainfrom
claude/export-interpreter-errors-b67bbd
Oct 9, 2026
Merged

Azorlogh merged 4 commits into
mainfrom
claude/export-interpreter-errors-b67bbd

Conversation

@Azorlogh

@Azorlogh Azorlogh commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Refs EN-2833

Hosts calling Run / ResolveDependencies (Formance Ledger) can only classify interpreter failures by string matching, because the error types live in internal/interpreter. Ledger currently reports client-caused failures (missing var, invalid account name, currency mismatch, meta() key missing, negative balance(), …) as internal errors.

Changes

  1. numscript.go: alias every error type the interpreter returns (same pattern as the existing Vm* block). Grouped and documented:
    • caused by the script, its vars, or the state it runs against (31 types);
    • QueryBalanceError / QueryMetadataError: a Store call failed, Unwrap yields the store's error;
    • InternalError / UnhandledError: numscript defects, fields diagnostic only.
      All are returned by value. Exported fields (other than the embedded source range) are declared part of the public contract — only added to, never renamed or removed.
  2. NegativeBalanceError gains Asset, so a host can report which balance was negative.
  3. errors_test.go: for each exported alias (except the two defect types, which no script can trigger), a script run through RunWithFeatureFlags — and ResolveDependencies where it fails the same way — must satisfy errors.As and carry the expected fields. Also checks ResolveDependencies returns ErrScalingNotSupported for scaling.

InvalidOperatorErr is not exported: nothing constructs it.

Follow-up

A numscript release is needed for Ledger to consume this.

Hosts calling Run or ResolveDependencies could only tell interpreter
failures apart by string matching, since all but MissingFundsErr and
NegativeAmountErr lived in internal/interpreter. Alias every error the
interpreter returns from the root package, document which are caused by
the script/vars/state versus numscript defects, and declare their fields
part of the public contract.

NegativeBalanceError now also carries the Asset whose balance was negative.

InvalidOperatorErr is not exported: nothing constructs it.

Refs EN-2833
@NumaryBot NumaryBot added risk: medium NumaryBot classified this pull request as medium risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-approved The NumaryBot review gate is satisfied for the current head. labels Oct 9, 2026
NumaryBot

This comment was marked as outdated.

paul-nicolas
paul-nicolas previously approved these changes Oct 9, 2026
@Azorlogh
Azorlogh dismissed stale reviews from paul-nicolas and NumaryBot via d7dfb0c October 9, 2026 13:04
@NumaryBot NumaryBot added risk: medium NumaryBot classified this pull request as medium risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-approved The NumaryBot review gate is satisfied for the current head. and removed risk: medium NumaryBot classified this pull request as medium risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-approved The NumaryBot review gate is satisfied for the current head. labels Oct 9, 2026
NumaryBot

This comment was marked as outdated.

@NumaryBot NumaryBot added risk: medium NumaryBot classified this pull request as medium risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-approved The NumaryBot review gate is satisfied for the current head. and removed risk: medium NumaryBot classified this pull request as medium risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-approved The NumaryBot review gate is satisfied for the current head. labels Oct 9, 2026
NumaryBot

This comment was marked as outdated.

ascandone
ascandone previously approved these changes Oct 9, 2026
@Azorlogh
Azorlogh dismissed stale reviews from ascandone and NumaryBot via 56a836b October 9, 2026 13:44
@NumaryBot NumaryBot added risk: medium NumaryBot classified this pull request as medium risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-approved The NumaryBot review gate is satisfied for the current head. and removed risk: medium NumaryBot classified this pull request as medium risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-approved The NumaryBot review gate is satisfied for the current head. labels Oct 9, 2026

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The required automated review completed with no remaining findings.

@Azorlogh
Azorlogh merged commit a549ff4 into main Oct 9, 2026
8 of 11 checks passed
@Azorlogh
Azorlogh deleted the claude/export-interpreter-errors-b67bbd branch October 9, 2026 13:49
Azorlogh added a commit to formancehq/ledger that referenced this pull request Oct 9, 2026
…errors

Dependency-resolution failures caused by the script, its vars, or the
state it read (invalid account name, missing or mistyped var, mismatched
currencies, bad portion, division by zero, missing or malformed
metadata, negative balance read, ...) returned Internal /
NUMSCRIPT_RUNTIME. numscript now exports the interpreter error types
(formancehq/numscript#215), so convertNumscriptError maps them to
ErrNumscriptExecution (NUMSCRIPT_EXECUTION_ERROR, FailedPrecondition).
Library defects (InternalError, UnhandledError) stay NUMSCRIPT_RUNTIME.

Admission: these failures are freezable, but a precondition failure
raised after a mutable balance/metadata read under an idempotency key
still forwards (PRELOAD_UNAVAILABLE), so the FSM can replay a key whose
original attempt succeeded before the state changed. Without a key or
without a mutable read it terminates on the execution error.

Bumps numscript to v0.1.2.
Azorlogh added a commit to formancehq/ledger that referenced this pull request Oct 9, 2026
…2206)

* fix(numscript): classify script execution failures as client errors

A script that runs and fails on its own terms (negative amount, allotment
not summing to 1, send-all from an unbounded source, posting amount wider
than 256 bits, ...) was returned as gRPC Internal / NUMSCRIPT_RUNTIME,
i.e. a server bug.

convertVMError now maps every script-caused public VM error type
(numscriptlib.Vm*Error) to the new ErrNumscriptExecution
(NUMSCRIPT_EXECUTION_ERROR, KindPrecondition), and produce() returns it
for negative and >256-bit postings. Precondition because amounts and
portions can come from balance()/meta(). The failure is now freezable
under an idempotency key, like INSUFFICIENT_FUNDS. VmInternalError,
undomained store errors and panics stay NUMSCRIPT_RUNTIME.

Errors raised during dependency resolution (interpreter) are unchanged:
the library exposes no public types for them.

Bumps the service protocol revision to 28 (response-semantic change).

* test(numscript): pin restore of frozen numscript execution failures

* fix(numscript): classify script-caused resolution failures as client errors

Dependency-resolution failures caused by the script, its vars, or the
state it read (invalid account name, missing or mistyped var, mismatched
currencies, bad portion, division by zero, missing or malformed
metadata, negative balance read, ...) returned Internal /
NUMSCRIPT_RUNTIME. numscript now exports the interpreter error types
(formancehq/numscript#215), so convertNumscriptError maps them to
ErrNumscriptExecution (NUMSCRIPT_EXECUTION_ERROR, FailedPrecondition).
Library defects (InternalError, UnhandledError) stay NUMSCRIPT_RUNTIME.

Admission: these failures are freezable, but a precondition failure
raised after a mutable balance/metadata read under an idempotency key
still forwards (PRELOAD_UNAVAILABLE), so the FSM can replay a key whose
original attempt succeeded before the state changed. Without a key or
without a mutable read it terminates on the execution error.

Bumps numscript to v0.1.2.

* test(e2e): cover frozen idempotency failures across restore

A keyed failure committed after the last full checkpoint is only in the
exported audit delta, and RebuildDelta re-freezes it from the audited
reason. No cluster test covered that for failures, only keyed successes.

The new suite puts three keyed failures in the delta and restores it:
INSUFFICIENT_FUNDS and NUMSCRIPT_EXECUTION_ERROR must replay their
original reason, message and metadata after their sources are funded,
and PRELOAD_UNAVAILABLE must re-execute once its meta() input exists.
Ends with a clean CheckStore.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot-reviewed NumaryBot completed its review workflow for the current head. review-approved The NumaryBot review gate is satisfied for the current head. risk: medium NumaryBot classified this pull request as medium risk.

Development

Successfully merging this pull request may close these issues.

4 participants