-
Notifications
You must be signed in to change notification settings - Fork 124
Resolve contract error names from WASM spec during invocation #2377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Resolve contract error names from WASM spec during invocation #2377
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Enhances contract invoke error reporting by resolving ScError::Contract(u32) numeric codes into human-readable error names/docs from the contract WASM spec and inserting that detail into the CLI error output.
Changes:
- Map RPC failures during simulation/submission through an error “enhancer” that extracts contract error codes and injects resolved name/doc into the message.
- Add
Spec::find_error_type_anyto locate an error case by numeric value across all error enums in the spec. - Update/extend tests to assert the new enriched error output behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| cmd/soroban-cli/src/commands/contract/invoke.rs | Adds contract error-code extraction and error-message enhancement using the contract spec; adjusts error display formatting; adds unit tests for parsing. |
| cmd/crates/soroban-test/tests/it/integration/custom_types.rs | Updates integration test expectations to validate enriched error output and the returned detail string. |
| cmd/crates/soroban-spec-tools/src/lib.rs | Adds find_error_type_any to search all error enums for a matching numeric error code. |
| Locator(#[from] locator::Error), | ||
|
|
||
| #[error("Contract Error\n{0}: {1}")] | ||
| #[error("{0}")] |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error::ContractInvoke now stores two strings but the thiserror display format only uses {0}. Since this enum is public and the tuple fields are now used as (enhanced message, detail), consider switching this variant to a struct variant with named fields (e.g., message, detail) or adding a brief doc comment describing what each field represents to prevent future misuse.
| #[error("{0}")] | |
| #[error("{0}")] | |
| /// The first `String` is the enhanced user-facing message; the second contains | |
| /// additional detail that is not included in the formatted error output. |
| let res = client | ||
| .send_transaction_polling(&config.sign(*txn, quiet).await?) | ||
| .await?; | ||
| .await | ||
| .map_err(|e| enhance_error(Error::Rpc(e), &spec))?; |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new submission-path enhancement (send_transaction_polling errors parsed via Contract(N)) isn’t covered end-to-end by the integration tests in this PR. Consider adding an integration test that exercises a transaction submission failure containing ScError::Contract(u32) and asserts the resolved error name/doc appear in the CLI output, so this path stays protected against regressions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏🏻 Very cool idea.
| /// The Debug format is used by `TransactionSubmissionFailed` errors which | ||
| /// pretty-print (`{:#?}`) the `TransactionResult`, where the number may | ||
| /// appear on a separate line with surrounding whitespace. | ||
| fn extract_contract_error_code(msg: &str) -> Option<u32> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be able to extract the error programatically, without string parsing, from the transaction result.
| /// "Error", this method searches across all error enums in the contract | ||
| /// spec. This handles contracts that include multiple error enums from | ||
| /// dependencies. | ||
| pub fn find_error_type_any( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For functions that have in their signature a Result return value with a specific error type, I think this logic should probably limit its search to that specific error type.
It's always possible for a contract to panic with some other error inside that function. But if a contract function says in its spec those are the errors it will return, we should probably limit displaying those errors.
What
When a contract invocation fails, resolve the numeric error code (e.g.
Error(Contract, #1)) to its human-readable name and documentation string from the contract's WASM spec, and display it directly next to the error code in the CLI output.Before:
❌ error: transaction simulation failed: HostError: Error(Contract, #1)
Event log (newest first):
0: [Diagnostic Event] ...
After:
❌ error: transaction simulation failed: HostError: Error(Contract, #1)
NotFound: The requested resource was not found.
Event log (newest first):
0: [Diagnostic Event] ...
Why
Contract authors define error enums with meaningful names and documentation in their Soroban contracts (e.g.
NotFound = 1with doc"The requested resource was not found."), but the CLI only showed the raw numeric code#1. This made debugging contract failures unnecessarily difficult — users had to manually cross-reference error codes against the contract source. The WASM spec already contains this information; the CLI just wasn't using it.This change handles all three error paths in contract invocation (first simulation, second simulation, and transaction submission), parses error codes from both the Display format (
Error(Contract, #N)) and Debug format (Contract(N)), and searches all error enums in the spec rather than only the one named "Error" to support contracts with multiple error enums from dependencies.Known limitations
Contract, #NandContract(N)). If the upstream error format changes, the parsing would need to be updated.\n\n) in the error string. If the RPC response format changes to not include a blank line separator, the resolved name would fall back to appearing at the end of the message.ScError::Contract(u32)codes are matched.