> Solana Developer Platform docs, served as Markdown. The [docs index](https://platform-beta.solana.com/docs.md) lists every page and [llms.txt](https://platform-beta.solana.com/docs/ai/llms.txt) the key ones; [SKILL.md](https://platform-beta.solana.com/SKILL.md) has the rules for building on SDP.

# Errors

A failed request returns an HTTP error status and one error envelope, with a code to branch on.

A failed request returns an error status and this body. The health checks are the exception: each answers with its own body, as its page shows.

```json
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Access denied"
  },
  "meta": { "requestId": "req_..." }
}
```

- `code` is stable: branch on it, not on `message`, which is for people and can change.
- `details`, when present, says more, such as which field failed validation.
- `meta.requestId` identifies the request. Quote it when you report a problem.

## Statuses

| Status | Means | Common codes |
| --- | --- | --- |
| `400` | The request is malformed or fails validation. | `BAD_REQUEST`, `IDEMPOTENCY_KEY_REQUIRED` |
| `401` | The API key is missing, invalid, expired, or revoked. | `UNAUTHORIZED`, `INVALID_API_KEY`, `EXPIRED_API_KEY`, `REVOKED_API_KEY` |
| `403` | The key lacks the grant or wallet access the request needs, or the call comes from an IP address the key or its organization doesn't allow (`details.reason` is `ip_not_allowed`). | `FORBIDDEN`, `INSUFFICIENT_PERMISSIONS` |
| `404` | Nothing with that ID exists in the project. | `NOT_FOUND` |
| `409` | The request conflicts with the resource's state, or an earlier request with the same idempotency key is still running or never recorded its outcome. | `CONFLICT`, `IDEMPOTENCY_KEY_IN_FLIGHT`, `IDEMPOTENCY_KEY_OUTCOME_UNKNOWN` |
| `413` | The request body is larger than 1 MB. | `PAYLOAD_TOO_LARGE` |
| `422` | The request is well formed but can't go ahead: its idempotency key was used for a different request, the transaction fee sponsor rejected the transaction, or the wallet's policy at its custody provider refused to sign it. | `IDEMPOTENCY_KEY_REUSED`, `SIGNING_REJECTED`, `CUSTODY_POLICY_REFUSED` |
| `429` | Too many requests. See [Rate limits](/docs/reference/conventions/rate-limits). | `RATE_LIMITED` |
| `500` | Something failed inside SDP. | `INTERNAL_ERROR` |
| `502` | Solana RPC or a provider answered, but with something SDP couldn't use, or the custody provider failed or timed out while signing. | `SOLANA_RPC_ERROR`, `PROVIDER_RESPONSE_INVALID`, `CUSTODY_ERROR` |
| `503` | SDP, or a provider it depends on, is unavailable for now, or a provider isn't configured for the project's environment. | `SERVICE_UNAVAILABLE`, `PROVIDER_UNAVAILABLE`, `PROVIDER_NOT_CONFIGURED`, `RPC_UNAVAILABLE` |

Each operation's page lists its statuses and shows a success example. The OpenAPI spec carries every response schema.

## Preconditions

A write that should apply only to the version you last read takes an optional `expectedUpdatedAt` field in its request body: the `updatedAt` from your last read of the resource. When the resource has changed since, the write changes nothing and returns `409`, carrying the current `updatedAt` in `error.details`. The operation's page says whether it takes the field, and names the code it returns. Omit it to write whatever the current version is.

The field is part of the body, so it is part of what an `Idempotency-Key` identifies: a retry under the same key must send the same value.

## Retrying

- `400`, `401`, `403`, `404`, `413`, and `422`: fix the request before you send it again.
- `422 CUSTODY_POLICY_REFUSED`: the wallet's policy at its custody provider refused to sign. The message names the provider. Review the policy there, or change the request, before you send it again.
- `409 CONFLICT`: read the resource's current state, then decide.
- `409` answering a stale `expectedUpdatedAt` (the code its operation's page names): the resource changed since the version you sent. Read it again, review what changed, then send the write with the new `updatedAt` and a new key.
- `409 IDEMPOTENCY_KEY_IN_FLIGHT` and `429`: wait for `Retry-After`, then retry with the same `Idempotency-Key`.
- `409 IDEMPOTENCY_KEY_OUTCOME_UNKNOWN`: final for that key. Read the resource with `GET` to see whether the request ran, then send it again with a new key.
- `503 RPC_UNAVAILABLE`: `details.reason` says why Solana RPC was unavailable. `upstream_unavailable` is temporary: wait for `Retry-After`, then retry as the `5xx` rule below says. `not_configured` and `cluster_mismatch` are SDP configuration problems that a retry won't fix. Quote `meta.requestId` when you report one.
- `503 PROVIDER_NOT_CONFIGURED`: a provider the request needs, such as the custody provider that signs for the wallet, isn't configured for the project's environment. A retry won't fix it until that changes. Check the provider's configuration, or quote `meta.requestId` when you report it.
- `5xx`: most routes store the error and return it again to a same-key retry. Read the resource with `GET` to see whether the request took effect, then retry with a new key. On the routes that move money, a same-key retry runs again and never acts twice.

[Idempotency](/docs/developing-with-sdp/idempotency) covers what each retry returns.
