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

> ParmanaApiError, server error codes, and retry strategies

## ParmanaApiError

All non-2xx HTTP responses from the server throw `ParmanaApiError`:

```typescript theme={null}
import { ParmanaClient, ParmanaApiError } from "@parmanasystems/sdk-client";

try {
  const attestation = await client.execute({ ... });
} catch (err) {
  if (err instanceof ParmanaApiError) {
    console.error(`HTTP ${err.status}: ${err.message}`);
  } else {
    // Network error, JSON parse error, etc.
    throw err;
  }
}
```

`ParmanaApiError` properties:

| Property  | Type                | Description                                   |
| --------- | ------------------- | --------------------------------------------- |
| `status`  | `number`            | HTTP status code                              |
| `message` | `string`            | Error message from the server's `error` field |
| `name`    | `"ParmanaApiError"` | Error class name                              |

***

## HTTP status codes

### 400 Bad Request

The request body is malformed or missing required fields.

```json theme={null}
{ "error": "body must have required property 'executionId'" }
```

Required fields for `POST /execute`: `executionId`, `policyId`, `policyVersion`, `signals`.

**Fix:** Validate the request body before sending. Ensure all required fields are present and correctly typed.

***

### 401 Unauthorized

The `Authorization` header is missing or the bearer token does not match `PARMANA_API_KEY`.

```json theme={null}
{ "error": "Unauthorized" }
```

**Fix:** Include `Authorization: Bearer <your-api-key>` on every request.

***

### 404 Not Found

The resource does not exist. Common cases:

* `GET /audit/decisions/:id` no decision with that `executionId`
* `POST /override` — no pending override for that `executionId`

```json theme={null}
{ "error": "Pending override not found" }
```

***

### 409 Conflict

An override was already resolved for this `executionId`.

```json theme={null}
{ "error": "Override already resolved" }
```

**Fix:** Check the current status with `client.audit.decision(executionId)` before submitting an override.

***

### 413 Request Entity Too Large

The request body exceeds 64 KB (65,536 bytes).

```json theme={null}
{ "error": "Request body too large" }
```

**Fix:** Reduce the size of the `signals` object. Remove any raw document or binary data. Signals should be structured scalar values.

***

### 422 Unprocessable Entity

The execution failed. The error message contains a structured error code.

```json theme={null}
{ "error": "[INV-013@replay] Replay detected: execution_fingerprint abc123 has already been consumed" }
```

Common 422 error codes:

| Code                        | Cause                                              | Action                                             |
| --------------------------- | -------------------------------------------------- | -------------------------------------------------- |
| `[INV-013@replay]`          | `executionId` already executed — replay blocked    | Use a new, unique `executionId`                    |
| `[INV-REPLAY-001]`          | No replay store configured on the server           | Server configuration issue                         |
| `[SYS-006]`                 | Policy evaluation returned undecided               | Policy has no catch-all rule — review policy logic |
| `[SYS-021]`                 | No pending execution found for override            | The `executionId` has no pending override          |
| `[SYS-022]`                 | Missing `execution_fingerprint` in pending context | Internal consistency error                         |
| `[SYS-023]`                 | Override lineage mismatch                          | Token/fingerprint mismatch                         |
| `[SYS-024]`                 | Replay store does not support override transitions | Server/store configuration issue                   |
| `[SYS-025]`                 | Missing provenance in pending override             | Internal consistency error                         |
| `Policy not found`          | Bundle missing from `PARMANA_POLICIES_ROOT`        | Deploy the policy bundle                           |
| `Signals validation failed` | Signals do not match `signalsSchema`               | Fix the signals payload                            |

***

### 429 Too Many Requests

Rate limit exceeded. `POST /execute` allows 100 requests per minute.

```json theme={null}
{
  "error": "Rate limit exceeded",
  "limit": 100,
  "remaining": 0,
  "reset": 1735689600
}
```

**Fix:** Implement exponential backoff. The `reset` field is a Unix timestamp when the rate limit window resets.

***

### 503 Service Unavailable

The governance runtime is not configured correctly missing signer, verifier, or replay store.

```json theme={null}
{ "error": "Governed execution runtime not configured" }
```

**Fix:** Check `GET /health`. If `verification !== "ok"`, the signing key is misconfigured. If `audit_db` is `false`, Postgres is disconnected (non-fatal for execution).

***

## Retry strategy

```typescript theme={null}
import type { ExecuteRequest, ExecutionAttestation } from "@parmanasystems/sdk-client";

async function executeWithRetry(
  client: ParmanaClient,
  request: ExecuteRequest,
  maxRetries = 3
): Promise<ExecutionAttestation> {
  let lastError: unknown;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await client.execute(request);
    } catch (err) {
      if (!(err instanceof ParmanaApiError)) {
        throw err;  // network error — re-throw
      }

      if (err.status === 422 && err.message.includes("[INV-013@replay]")) {
        throw err;  // replay block — do not retry, use a new executionId
      }

      if (err.status === 400 || err.status === 401 || err.status === 413) {
        throw err;  // client error — do not retry
      }

      if (err.status === 429) {
        // Rate limited — wait until reset
        await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
        lastError = err;
        continue;
      }

      if (err.status >= 500) {
        // Server error — retry with backoff
        if (attempt < maxRetries) {
          await new Promise(r => setTimeout(r, 500 * Math.pow(2, attempt)));
          lastError = err;
          continue;
        }
      }

      throw err;
    }
  }

  throw lastError;
}
```

**Do not retry 422 errors with the same `executionId`.** A 422 from `[INV-013@replay]` means the ID was consumed. Retrying with the same ID will replay-block again. Use a new `executionId` for a new execution.

***

## Network errors

If `fetch` throws (DNS failure, connection refused, timeout), the error is not a `ParmanaApiError` it is a native `TypeError` or similar. Handle these separately:

```typescript theme={null}
try {
  const attestation = await client.execute({ ... });
} catch (err) {
  if (err instanceof ParmanaApiError) {
    // Server returned a structured error
    handleServerError(err);
  } else if (err instanceof TypeError && err.message.includes("fetch")) {
    // Network-level failure
    handleNetworkError(err);
  } else {
    throw err;
  }
}
```

***

## Troubleshooting

**All requests return 503** Check `GET /health`. The server's signer or verifier is not configured. This is a server-side configuration issue, not a client issue.

**422 with no recognizable error code** Execution threw an unexpected error. Check server logs for the full stack trace. Look for `execute:failure` log entries with the `error` field.

**Verification returns `valid: false`** Do not proceed with the authorized action. The attestation was tampered with or produced by an untrusted runtime. Investigate before taking any business action.

**429 on every request** The client is exceeding 100 requests per minute on `POST /execute`. Implement rate limiting on the client side or distribute load across multiple API keys.
