# Errors

> The error codes the API returns, what causes each, and how to fix it.

Errors come back with an HTTP status and a JSON body:

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_cursor",
    "message": "The cursor is not valid for this endpoint.",
    "requestId": "req_xY3kq9Lm2pRt4Vw8"
  }
}
```

Branch on `code`; the `message` can change. `type` is `api_error` for `5xx` and `invalid_request_error` otherwise. Handle a code you don't recognize by its HTTP status. Send the `requestId` to `hello@finchnode.com` when you ask for help.

Simulation failures don't arrive as errors. Read `simulation.failureCode` on the session instead.

Over MCP, a bad key or an address limit fails the whole request with a JSON-RPC error and HTTP `401` or `429`. Tool errors carry the codes below. See [MCP server](/docs/ai-agents/mcp-server).

## Authentication

| Code | Status | Cause | Fix |
| --- | --- | --- | --- |
| `missing_api_key` | 401 | The `Authorization` header is missing, or it is not `Bearer` followed by a key. | Send `Authorization: Bearer $FINCHNODE_API_KEY` from your server. |
| `invalid_api_key` | 401 | The key is unknown, revoked, or malformed. | Copy a current key from the console. Keys are shown once, so create a new one if you lost it. |
| `invalid_agent_token` | 401 | The agent token is unknown, expired, or revoked, was issued for another API, or its authority ended. | Delegate a new credential with `POST /agent-credentials`, and use it with the REST API. |
| `rate_limited` | 429 | A key, an app's delegated credentials, or a network address went over its limit, or a conformance run forced a `429`. | Wait for the `Retry-After` seconds, then retry with backoff. See Rate limits. |

## Request

| Code | Status | Cause | Fix |
| --- | --- | --- | --- |
| `invalid_json` | 400 | The body is malformed JSON, or JSON that is not an object. | Send `Content-Type: application/json` with a well-formed body. |
| `payload_too_large` | 413 | The body is over 64 KiB. | Send only the documented fields. |
| `invalid_request` | 400 | A field is missing, unknown, or out of range, or an application setting refuses the request. A few cases return `403` or `415`. | Read `error.message` and the status, then compare the request with the API reference. |
| `invalid_idempotency_key` | 400 | The `Idempotency-Key` header on `POST /connect/sessions` is longer than 200 characters. | Send a unique key of 200 characters or fewer per logical request. |
| `idempotency_conflict` | 409 | The `Idempotency-Key` was already used with a different request body. | Reuse a key only to retry the identical request. Send a new key for a new request. |
| `invalid_categories` | 400 | A category is not one of the ten record categories, a single-category endpoint got more than one, or the scenario can share none of them. | Use the category keys from Record model and categories. |
| `invalid_pagination` | 400 | `limit` is not a whole number from 1 to 100, or `offset` is not a whole number of 0 or more. | Send a `limit` from 1 to 100. Prefer `cursor` over `offset`. |
| `invalid_cursor` | 400 | The cursor came from a different endpoint, subject, or category, or it was edited. | Start a change feed from `meta.changeCursor`, then pass back each `nextCursor` unchanged. |

## Access

| Code | Status | Cause | Fix |
| --- | --- | --- | --- |
| `not_found` | 404 | No resource with that ID is visible to this key, or the path is wrong. | Check the ID and the environment. Sandbox and live keys see different data. |
| `operation_forbidden` | 403 | This credential type cannot perform the operation, for example a patient-scoped agent credential creating a session. | Call the operation with your application key. |
| `app_scope_exceeded` | 403 | The request asks for a category your application is not configured to read. | Add the category to the application in the console, or drop it from the request. |
| `credential_scope_exceeded` | 403 | The delegated credential does not cover that category or receipt. | Delegate a credential that includes the category. |
| `subject_mismatch` | 403 | The credential is bound to a different subject. | Use the credential issued for this subject, or your application key. |
| `authority_changed` | 403 | Consent or credential authority changed while the read was running. | Retry the read. If it fails again, check the consent receipt. |
| `consent_inactive` | 410 | The patient no longer has an active consent receipt for your application. | Stop reading this subject and apply your retention policy. Reading again needs a new share from the patient, and a new delegated credential. |
| `consent_scope_exceeded` | 403 | Your app may read the category, but this patient did not share it. A delegated credential that asks for it gets the same code. | Read only the categories on the patient's receipt. A new Connect session can ask for more. |
| `invalid_grant` | 400 | A delegated credential request has no categories, a blank or too-long purpose, or unsupported operations. | Send `subject`, `categories`, and a `purpose` of 500 characters or fewer. Leave out `operations` or list supported ones. |
| `invalid_duration` | 400 | `durationSeconds` is not a whole number from 1 to 3600. | Set `durationSeconds` to a whole number from 1 to 3600. |
| `authority_unavailable` | 503 | FinchNode could not check the credential's authority right now. | Retry with backoff. |
| `app_suspended` | 403 | The application is suspended. | Email `hello@finchnode.com`. |
| `production_not_enabled` | 403 | A live key was used on an application that has not turned on production. | Turn on production for the application in the console, or use a sandbox key. |

## Connect sessions

| Code | Status | Cause | Fix |
| --- | --- | --- | --- |
| `session_completed` | 409 | You tried to cancel a session that already completed. | Nothing to cancel. Read the session for its subject. |
| `session_expired` | 410 | The session URL expired before the patient finished. | Create a new session. |

## Sandbox

| Code | Status | Cause | Fix |
| --- | --- | --- | --- |
| `sandbox_only` | 403 | Simulation and sandbox controls work only with sandbox (`ck_test_`) keys. | Use a sandbox key. |
| `sandbox_unavailable` | 503 | The scenario sandbox is not available on this deployment yet. | Retry later, or use the demo API in the meantime. |
| `invalid_scenario` | 400 | The scenario name is not one of the named scenarios, or it does not fit this request. | Pick a scenario from Scenarios. |
| `session_not_simulatable` | 409 | The session cannot be simulated: it already completed, or it is bound to another scenario. | Create a new sandbox session and simulate that. |
| `sandbox_limit_reached` | 409 | The application has the maximum number of active synthetic patients. | Wait for older synthetic patients to be purged, or reuse one. |
| `subject_not_connected` | 409 | The synthetic patient has no connected source for this control. | Simulate a session that connects a source first. |
| `control_unsupported` | 409 | This scenario does not support that sandbox control. | Pick a scenario whose controls include it. See Sandbox controls. |
| `invalid_event_type` | 400 | The event type is not a sandbox control. | Use `records.advance`, `consent.revoke`, `consent.expire`, or `source.fail`. |
| `invalid_source` | 400 | The source is not one of this synthetic patient's sources. | Name a source from the subject's record metadata. |

## Conformance

| Code | Status | Cause | Fix |
| --- | --- | --- | --- |
| `app_not_sandbox` | 409 | Conformance runs need an application in sandbox status. | Run conformance on a separate sandbox application. |
| `run_in_progress` | 409 | This application already has an open conformance run. | Finish or cancel the open run first. |
| `run_not_open` | 409 | The conformance run already ended. | Start a new run. |
| `phase_not_advanceable` | 409 | The run's session phase ends when its Connect session completes, or probes are still in flight. | Complete the session, wait for probes, or cancel the run. |
| `conformance_unavailable` | 503 | Conformance runs are not available on this deployment yet. | Retry later. |

## Platform

| Code | Status | Cause | Fix |
| --- | --- | --- | --- |
| `database_error` | 500 | FinchNode could not complete the request. | Retry with backoff. Send the `requestId` to support if it persists. |
| `internal_error` | 500 | An unexpected server error. | Retry with backoff. Send the `requestId` to support if it persists. |
