Skip to main content

ParmanaApiError

All non-2xx HTTP responses from the server throw ParmanaApiError:
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:
PropertyTypeDescription
statusnumberHTTP status code
messagestringError 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.
{ "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.
{ "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
{ "error": "Pending override not found" }

409 Conflict

An override was already resolved for this executionId.
{ "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).
{ "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.
{ "error": "[INV-013@replay] Replay detected: execution_fingerprint abc123 has already been consumed" }
Common 422 error codes:
CodeCauseAction
[INV-013@replay]executionId already executed — replay blockedUse a new, unique executionId
[INV-REPLAY-001]No replay store configured on the serverServer configuration issue
[SYS-006]Policy evaluation returned undecidedPolicy has no catch-all rule — review policy logic
[SYS-021]No pending execution found for overrideThe executionId has no pending override
[SYS-022]Missing execution_fingerprint in pending contextInternal consistency error
[SYS-023]Override lineage mismatchToken/fingerprint mismatch
[SYS-024]Replay store does not support override transitionsServer/store configuration issue
[SYS-025]Missing provenance in pending overrideInternal consistency error
Policy not foundBundle missing from PARMANA_POLICIES_ROOTDeploy the policy bundle
Signals validation failedSignals do not match signalsSchemaFix the signals payload

429 Too Many Requests

Rate limit exceeded. POST /execute allows 100 requests per minute.
{
  "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.
{ "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

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