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

# Error Model

> The canonical error response structure — and where implementation diverges from spec

## Specified error model

The platform's normative error specification (`docs/api/03-error-model.md`) defines every error response as:

```json theme={null}
{
  "error": {
    "code": "POLICY_NOT_FOUND",
    "message": "Requested policy version does not exist."
  }
}
```

with `code` as a stable, machine-readable identifier and `message` as a human-readable description, and a fixed set of canonical codes grouped by category:

| Category       | Codes                                                                                                                      |
| -------------- | -------------------------------------------------------------------------------------------------------------------------- |
| Validation     | `INVALID_REQUEST`, `INVALID_METADATA`, `INVALID_POLICY`, `INVALID_SIGNALS`, `INVALID_SCHEMA`                               |
| Authentication | `UNAUTHENTICATED`, `INVALID_TOKEN`, `TOKEN_EXPIRED`                                                                        |
| Authorization  | `FORBIDDEN`, `INSUFFICIENT_PERMISSIONS`                                                                                    |
| Resource       | `BUSINESS_TRANSACTION_NOT_FOUND`, `EXECUTION_NOT_FOUND`, `RECEIPT_NOT_FOUND`, `POLICY_NOT_FOUND`, `VERIFICATION_NOT_FOUND` |
| Conflict       | `DUPLICATE_TRANSACTION`, `OVERRIDE_NOT_ALLOWED`, `IMMUTABLE_RESOURCE`                                                      |
| Verification   | `VERIFICATION_FAILED`, `INVALID_SIGNATURE`, `TRUST_RECORD_CORRUPTED`, `POLICY_MISMATCH`                                    |
| Internal       | `INTERNAL_ERROR`, `STORAGE_FAILURE`, `UNEXPECTED_ERROR`                                                                    |

| HTTP Status | Category                  | Example                 |
| ----------- | ------------------------- | ----------------------- |
| 400         | Validation                | `INVALID_REQUEST`       |
| 401         | Authentication            | `UNAUTHENTICATED`       |
| 403         | Authorization             | `FORBIDDEN`             |
| 404         | Resource                  | `POLICY_NOT_FOUND`      |
| 409         | Conflict                  | `DUPLICATE_TRANSACTION` |
| 422         | Verification / Validation | `VERIFICATION_FAILED`   |
| 500         | Internal                  | `INTERNAL_ERROR`        |

## What the implementation actually returns today

<Warning>
  **Discrepancy.** `packages/api/src/middleware/error-handler.ts` does not use the nested `{ "error": { "code", "message" } }` structure above. It returns a flat shape:

  ```json theme={null}
  { "error": "Requested policy version does not exist." }
  ```

  and only attaches a top-level `code` field for errors that are instances of `RuntimeError`:

  ```json theme={null}
  { "error": "...", "code": "SOME_RUNTIME_CODE" }
  ```

  Individual route handlers (`execute.ts`, `receipt.ts`, `replay.ts`, `verify.ts`) also return their own ad hoc `{ "error": "..." }` bodies directly for request-shape validation (e.g. missing/invalid `businessTransactionId`), before the transaction even reaches the error handler.
</Warning>

## Practical status mapping (as implemented)

| Route                                 | Failure condition                          | Status         | Body shape                               |
| ------------------------------------- | ------------------------------------------ | -------------- | ---------------------------------------- |
| `POST /execute`                       | Invalid or missing `businessTransactionId` | 400            | `{ "error": string }`                    |
| `POST /policies/validate`             | Missing `policyId` / `policyVersion`       | 400            | `{ "valid": false, "errors": string[] }` |
| `POST /policies/validate`             | Policy not found                           | 404            | `{ "valid": false, "errors": string[] }` |
| `GET /transactions/:id`               | Not found                                  | 404            | `{ "error": string }`                    |
| `GET /trust-records/:id`              | Not found                                  | 404            | `{ "error": string }`                    |
| `GET /verify/:id`, `GET /receipt/:id` | Trust record or sub-resource not found     | 404            | `{ "error": string }`                    |
| Anything else                         | `RuntimeError` subclass                    | `error.status` | `{ "error": string, "code": string }`    |
| Anything else                         | Unhandled exception                        | 500            | `{ "error": "Internal Server Error" }`   |

## Recommendation for integrators

Match on the `error` string's presence and HTTP status code rather than a `code` field, until the error handler is updated to conform to the specified nested model. If you need stable machine-readable codes today, they're only reliably present for errors that subclass `RuntimeError`.
