Skip to main content
Most API errors are about the request: a missing field, an unknown id. An invariant violation is different — the request was well-formed, but completing it would put the business in a state the engine refuses to represent: a refund larger than what was captured, a journal entry that does not balance, a return of more units than shipped. Those errors carry a third field, invariant, alongside the usual code and message:
code and the HTTP status are what they would have been without the invariant. invariant is a stable identifier: it does not change between releases, it is the same across the REST API, the embedded engine and every language binding, and it is what an agent should branch on. The message is for humans and may be reworded.
Branch on error.invariant, not on error.message. Every code below is pinned by the engine’s conformance suite, so a handler written against one keeps working; a handler that pattern-matches the message will not.

The codes

Where they surface

  • REST — in the error.invariant field of the JSON error body, as above.
  • Embedded engine and bindings — as the same string on the thrown error, so a Node or Python handler can switch on it exactly as an HTTP client would.
  • MCP tools — in the tool error result, unchanged.
The engine’s conformance suite asserts every code above in all four implementations under test, so the list is complete for this release rather than a sample.
  • Errors — the general error envelope and HTTP status contract
  • Money precision — why scale_exceeds_currency exists