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

# Error Catalog

> Every error this API can return: HTTP status, code (when present), the exact condition that produces it, and what a caller should do. Assembled from live-triggered captures, not inferred from code.

<Info>**\[AVAILABLE]**. Every row below was triggered against a live server this session or a
previous session that produced [REST API Introduction](/api-reference/introduction) or the
TypeScript SDK fix pass, not inferred from reading source alone. See [Error
handling](/api-reference/error-handling) for the envelope shape this table assumes.</Info>

## Authentication

| Status | Code | Message                   | Condition                                                                                        | What to do                                                                                                      |
| ------ | ---- | ------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| 401    | none | `authentication required` | `Authorization` header missing, not a `Bearer <key>`, or the key doesn't match a configured hash | Send a valid key from [Authentication](/api-reference/authentication). Not retryable without fixing the header. |

## Authorization (caller is known; not permitted to act as this principal)

| Status | Code | Message                                                         | Condition                                                                                                                                                                                                                                                                                                              | Routes                                | What to do                                                                                                                                                                      |
| ------ | ---- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 403    | none | `Caller is not permitted to assert this authority.principalId.` | `transaction.authority.principalId` is missing/empty, or doesn't equal the authenticated `callerId` (default, no `allowedPrincipalIds` configured for this key), or isn't present in that key's configured `allowedPrincipalIds` list (`isPrincipalAllowed`, `packages/api/src/routes/execute.ts` / `transactions.ts`) | `POST /execute`, `POST /transactions` | Set `authority.principalId` to an identity this caller's key is actually permitted to assert. Skipped entirely (no 403 possible) when caller authentication itself is disabled. |

## Policy denial (request well-formed; the business rule said no)

| Status | Code            | Message                                        | Condition                                                                                                                                                              | Routes                                | What to do                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| ------ | --------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 403    | `POLICY_DENIED` | `Execution rejected: <policy-specific reason>` | Policy evaluation returned `REJECTED` (`ExecutionGate.enforce`, `packages/runtime/src/ExecutionGate.ts`, thrown with an explicit `status: 403, code: "POLICY_DENIED"`) | `POST /execute`, `POST /transactions` | This is a real, observed pipeline outcome, not a bug: the request was well-formed but the business decision was no. Read the reason string; it names the specific Policy condition that failed. Distinct from the caller-identity 403 above — that one is about who the caller may act as, this one is about whether the requested action itself is allowed. Previously indistinguishable from a genuine server error (both surfaced as a generic `500`) — corrected, see `docs/CLAIMS.md` §2.21. |

## Validation (caller's request is malformed)

| Status | Code | Message                                                                                 | Condition                                                                                     | Routes                                                                 | What to do                                                                                                                    |
| ------ | ---- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| 400    | none | `businessTransactionId must be a valid UUID.`                                           | `businessTransactionId` present but not a valid UUID v1-v5                                    | `POST /execute`, `POST /transactions`, `POST /verify`, `POST /receipt` | Fix the ID format. Not a transient failure.                                                                                   |
| 400    | none | `businessTransactionId is required.`                                                    | `businessTransactionId` missing entirely                                                      | `POST /replay`                                                         | Include the field. `/replay` checks presence only, not UUID format, unlike the routes above.                                  |
| 400    | none | `authorization.authorityId must match authority.authorityId.`                           | A Business Transaction trust-chain invariant failed (`BusinessTransactionValidationError`)    | `POST /execute`, `POST /transactions`                                  | Fix the request body's internal consistency; this is one example invariant among several the shared validator enforces.       |
| 400    | none | `{"valid":false,"errors":["policyId is required."]}` / `["policyVersion is required."]` | `policyId` or `policyVersion` missing/empty, checked in that order, first miss short-circuits | `POST /policies/validate`                                              | Supply both fields. This route never uses the shared `{error}` envelope, see [Error handling](/api-reference/error-handling). |

## Not found

| Status | Code                  | Message                                                                           | Condition                                                                                                                 | Routes                                                                          | What to do                                                                                                                            |
| ------ | --------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| 404    | none                  | `Policy '<name>' version '<version>' was not found.`                              | `transaction.policy.name`/`version` doesn't match any published Policy (`PolicyNotFoundError`)                            | `POST /execute`, `POST /transactions`                                           | Publish the policy first, or fix the name/version in the request.                                                                     |
| 404    | none                  | `{"valid":false,"errors":["Policy '<name>' version '<version>' was not found."]}` | Same condition as above, different envelope                                                                               | `POST /policies/validate`                                                       | Same fix; this route reshapes the error locally instead of using the shared handler.                                                  |
| 404    | none                  | `Business Transaction not found.`                                                 | No Business Transaction exists for the path ID                                                                            | `GET /transactions/{id}`                                                        | Confirm the ID; nothing to retry.                                                                                                     |
| 404    | `VERIFICATION_FAILED` | `Execution Trust Record not found.`                                               | No Execution Trust Record exists for `businessTransactionId` (`VerificationFailedError`, reused across three POST routes) | `POST /verify`, `POST /receipt`, `POST /replay`                                 | Confirm the record exists via `GET /trust-records/{id}` first.                                                                        |
| 404    | none                  | `Execution Trust Record not found.`                                               | Same underlying condition, but reached via a GET, no `code` field on this path                                            | `GET /verification/{id}`, `GET /receipt/latest/{id}`, `GET /trust-records/{id}` | Same fix; note the `code` field's presence depends on which route you called, not on the underlying cause.                            |
| 404    | none                  | `Verification not found.`                                                         | Record exists but has no Verification yet                                                                                 | `GET /verification/{id}`                                                        | In practice every Execution produces a Verification synchronously, this is an edge case for records that haven't completed that step. |
| 404    | none                  | `Receipt not found.`                                                              | Record exists but has no Receipt yet                                                                                      | `GET /receipt/latest/{id}`                                                      | Call `POST /receipt` first, or the record's Execution hasn't reached that stage.                                                      |

## Rate limited

| Status | Code           | Message                                 | Condition                                                                                                                                                                    | Routes                                       | What to do                                                                                                                                                                                                                                         |
| ------ | -------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 429    | `RATE_LIMITED` | `Rate limit exceeded. Try again later.` | Caller (by authenticated identity, not IP) exceeded the configured `/execute` rate limit, or the caller's IP exceeded the separate, more permissive `/health`/`/ready` limit | `POST /execute`, `GET /health`, `GET /ready` | Back off; the response carries a `Retry-After` header. Not a transient server issue — no nonce is consumed and nothing is signed for a request rejected here. See [Authentication](/api-reference/authentication#rate-limiting) for the mechanism. |

## Conflict

| Status | Code                        | Message                                                                                   | Condition                                                                                                     | Routes                                | What to do                                                                                                                                        |
| ------ | --------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| 409    | none                        | `Business Transaction '<id>' already exists.`                                             | A Business Transaction with this `businessTransactionId` already exists (`DuplicateBusinessTransactionError`) | `POST /execute`, `POST /transactions` | This is not a transient failure to retry. Fetch the existing record instead, see [Idempotency and nonces](/api-reference/idempotency-and-nonces). |
| 409    | `RECEIPT_GENERATION_FAILED` | `Execution Trust Record must be successfully verified before a Receipt can be generated.` | The record exists but its latest Verification isn't `VERIFIED`                                                | `POST /receipt`                       | Call `POST /verify` first and confirm `status: VERIFIED` before requesting a Receipt.                                                             |

<Note>
  **Not listed here on purpose:** `NonceAlreadyConsumedError` (409, code
  `NONCE_ALREADY_CONSUMED`) is a real, tested error `error-handler.ts` maps — thrown by
  `ExecutionGateway.execute()` when every other verification check passed and only nonce
  consumption failed (an isolated replay of an already-executed authorization). It is not
  reachable through any of the 14 routes this catalog covers: a resubmitted
  `businessTransactionId` is always caught first by the `DuplicateBusinessTransactionError` 409
  above, before a second authorization — and thus a second nonce — is ever generated. It's a
  library-level guarantee for a receiving system calling `@parmana/execution-gateway` directly,
  not an HTTP-reachable outcome of Parmana's own server. See `docs/CLAIMS.md` §2.21 for the
  exact scope.
</Note>

## Server-side (500)

| Status | Code | Message                 | Condition                                                                                                                                                                                                                                                                                                                                      | Routes                                | What to do                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------ | ---- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 500    | none | `Internal Server Error` | A structurally incomplete but UUID-valid body (for example, `businessTransactionId` present but `metadata` missing) throws an unhandled `TypeError` inside `BusinessTransactionValidator.validate` before any typed error class is constructed                                                                                                 | `POST /execute`, `POST /transactions` | Send every required nested object from the [OpenAPI reference](/api-reference/introduction)'s request schema. The generic message hides the real cause; it is visible only in server logs.                                                                                                                                                                                                                                       |
| 500    | none | `Internal Server Error` | **Regression, verified in an earlier pass, not yet fixed:** no Connector is registered for the request's `action`. Previously reached the client as a coded `RuntimeError` (`"No connector registered for action: <action>.", code: RUNTIME_ERROR`); it no longer does, see [Error handling](/api-reference/error-handling) for the mechanism. | `POST /execute`, `POST /transactions` | Use a registered `action`. Nothing is wired by default outside `NODE_ENV=test` (which registers `test:fixture-execute`); `razorpay:refund-create` and `hubspot:deal-update` register only when their respective credentials are configured, see [Connector Development Guide](/integrations/connector-development-guide). This is flagged as a product regression to fix in `packages/connector-sdk`, not a documentation issue. |

## What's deliberately not here

This catalog lists errors from the 14 routes in `packages/api/src/app.ts`. It does not cover
exceptions raised only in code paths unreachable from the HTTP API (for example, internal
validator classes that never escape a route handler uncaught), or client-side SDK exceptions,
see [Python SDK](/sdks/python) and [TypeScript SDK](/sdks/typescript) for how each wraps these
same HTTP responses.
