> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Invariant error codes

> The 24 stable commerce.* codes an agent can branch on when the engine refuses a write that would violate a business invariant.

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:

```json theme={null}
{
  "error": {
    "code": "conflict",
    "message": "refund of 120.00 exceeds captured amount 100.00",
    "invariant": "commerce.refund.exceeds_captured"
  }
}
```

`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.

<Tip>
  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.
</Tip>

## The codes

| Invariant                                            | The write was refused because…                                                                                                                           |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `commerce.money.scale_exceeds_currency`              | an amount carried more decimal places than the currency allows — cents for USD, none for JPY. Money is exact-decimal; the engine will not round for you. |
| `commerce.capture.exceeds_order_total`               | a payment capture would exceed the order total                                                                                                           |
| `commerce.refund.exceeds_captured`                   | a refund would exceed what has actually been captured                                                                                                    |
| `commerce.return.order_not_shipped`                  | a return was opened against an order that has not shipped                                                                                                |
| `commerce.return.exceeds_shipped`                    | a return line asks for more units than were shipped                                                                                                      |
| `commerce.inventory.insufficient_available`          | a reservation or allocation asks for more than is available at the location                                                                              |
| `commerce.inventory.location_not_found`              | the named location does not exist                                                                                                                        |
| `commerce.inventory.sku_conflict`                    | the SKU is already in use                                                                                                                                |
| `commerce.product.sku_conflict`                      | the product's SKU is already in use                                                                                                                      |
| `commerce.product.slug_conflict`                     | the product's slug is already in use                                                                                                                     |
| `commerce.checkout.cart_not_found`                   | the checkout referenced a cart that does not exist                                                                                                       |
| `commerce.checkout.conflict`                         | the checkout collided with a concurrent change to the same cart                                                                                          |
| `commerce.checkout.rejected`                         | the checkout was rejected by a configured gate (tax, shipping, or stock failed closed)                                                                   |
| `commerce.checkout.validation_failed`                | the checkout payload failed validation                                                                                                                   |
| `commerce.ledger.entry_unbalanced`                   | a journal entry's debits and credits do not balance                                                                                                      |
| `commerce.ledger.line_not_single_sided`              | a journal line carries both a debit and a credit                                                                                                         |
| `commerce.ledger.entry_not_found`                    | the journal entry does not exist                                                                                                                         |
| `commerce.ledger.entry_not_postable`                 | the journal entry is not in a state that can be posted                                                                                                   |
| `commerce.ledger.validation_failed`                  | the journal entry failed validation                                                                                                                      |
| `commerce.subscription.billing_cycle_not_found`      | the billing cycle does not exist                                                                                                                         |
| `commerce.subscription.billing_cycle_not_chargeable` | the billing cycle is not in a chargeable state                                                                                                           |
| `commerce.subscription.non_positive_charge`          | a subscription charge was zero or negative                                                                                                               |
| `commerce.subscription.charge_validation_failed`     | the charge payload failed validation                                                                                                                     |
| `commerce.subscription.validation_failed`            | the subscription payload failed validation                                                                                                               |

## 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.

## Related

* [Errors](/api-reference/errors) — the general error envelope and HTTP status contract
* [Money precision](/stateset-icommerce/stateset-icommerce-bindings#money-precision) — why `scale_exceeds_currency` exists
