Repository navigation
feat: export interpreter error types for errors.As classification - #215
Merged
Merged
Conversation
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
paul-nicolas
previously approved these changes
Oct 9, 2026
ascandone
previously approved these changes
Oct 9, 2026
NumaryBot
approved these changes
Oct 9, 2026
NumaryBot
left a comment
Contributor
There was a problem hiding this comment.
The required automated review completed with no remaining findings.
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.
3 tasks done
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs EN-2833
Hosts calling
Run/ResolveDependencies(Formance Ledger) can only classify interpreter failures by string matching, because the error types live ininternal/interpreter. Ledger currently reports client-caused failures (missing var, invalid account name, currency mismatch,meta()key missing, negativebalance(), …) as internal errors.Changes
numscript.go: alias every error type the interpreter returns (same pattern as the existingVm*block). Grouped and documented:QueryBalanceError/QueryMetadataError: aStorecall failed,Unwrapyields 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.
NegativeBalanceErrorgainsAsset, so a host can report which balance was negative.errors_test.go: for each exported alias (except the two defect types, which no script can trigger), a script run throughRunWithFeatureFlags— andResolveDependencieswhere it fails the same way — must satisfyerrors.Asand carry the expected fields. Also checksResolveDependenciesreturnsErrScalingNotSupportedfor scaling.InvalidOperatorErris not exported: nothing constructs it.Follow-up
A numscript release is needed for Ledger to consume this.