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

> The shared error envelope, when the code field is present versus absent, and the two verified gaps where implementation doesn't match the envelope's own design.

<Info>**\[AVAILABLE]**. `packages/api/src/middleware/error-handler.ts`, verified against a live server in this session.</Info>

## The envelope

Every non-2xx response (except `POST /policies/validate`, see below) is:

```json theme={null}
{ "error": "string, always present, human-readable" }
```

sometimes with a second field:

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

`code` is present **only** when the failure reached the shared error handler as a typed
`RuntimeError` subclass: `VerificationFailedError` (`VERIFICATION_FAILED`),
`ReceiptGenerationError` (`RECEIPT_GENERATION_FAILED`), or an uncategorized `RuntimeError`
(`RUNTIME_ERROR`). It is absent from every inline route-level check
(`businessTransactionId` format/required checks), from
`BusinessTransactionValidationError`, `PolicyValidationError`, `SignalValidationError`,
`PolicyNotFoundError`, `DuplicateBusinessTransactionError`, and from the generic 500
fallback that catches everything else.

`error` is always a plain string, never a nested object, this is deliberate: a caller can
always safely display it or log it without walking a schema first.

**`POST /policies/validate` does not use this envelope at all.** Every status code it can
return (200, 400, 404) is `{"valid": boolean, "errors": string[]}`, its own shape,
independent of what the caller sent. The one exception is 401, which is generated by the
caller-auth middleware *before* this handler ever runs, so a rejected caller sees the shared
envelope (`{"error": "authentication required"}`), not `{valid, errors}`.

## Why `code` is inconsistent by design, not by accident

The envelope was built to describe **whatever the code actually raises**, not a target
contract layered on afterward. Two verified gaps below show what that means in practice:
places where an error path that looks like it should carry a stable `code` in fact does not,
because the exception that reaches the handler isn't the typed class the handler's
`instanceof` checks expect.

## Two verified gaps

### 1. A structurally incomplete body crashes uncoded

A `POST /execute` or `POST /transactions` body with a valid-UUID `businessTransactionId` but
a missing required nested object (for example `metadata`) does not reach the 400
`BusinessTransactionValidationError` path the schema implies exists for it. It throws an
unhandled `TypeError` deep inside `BusinessTransactionValidator.validate`
(`packages/runtime/src/validators/BusinessTransactionValidator.ts:14`), which is not an
instance of `BusinessTransactionValidationError`, so the handler's `instanceof` check never
matches. The caller sees only:

```json theme={null}
{ "error": "Internal Server Error" }
```

with a 500, no `code`, and no indication of which field was missing. The real cause is
visible only in the server's own log.

### 2. A regression: "no Connector registered" lost its code, this pass

Re-verified live against the current server, not carried over from an earlier capture: the
"no Connector registered for this action" case used to reach the client as a coded
`RuntimeError` (`{"error":"No connector registered for action: <action>.","code":
"RUNTIME_ERROR"}`). It no longer does. `ConnectorSdkRegistry.resolveCapability`
(`packages/connector-sdk/src/ConnectorRegistry.ts`) now throws a raw, uncaught `Error: No
connector registered for capability '<capability>'.` that also fails the handler's
`instanceof RuntimeError` check, collapsing into the exact same shape as gap #1:

```json theme={null}
{ "error": "Internal Server Error" }
```

This is listed here as a **product regression to fix in
`packages/connector-sdk`/`packages/execution-control`**, most likely a side effect of the
recent capability-routing refactor, not a documentation change made in this pass. See the
[Error catalog](/api-reference/error-catalog) for the full, current, real behavior of every
route.

## Cross-reference

Every status code documented in the OpenAPI reference links to its entry in the [Error
catalog](/api-reference/error-catalog), which lists the triggering condition and what a
caller should do for each one.
