> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sqd.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Reach for SQD when you need onchain data without running a node or an indexer: decoded EVM logs and transactions, Solana instructions, Bitcoin transactions, Substrate events and calls, or Hyperliquid fills, over any block range on 120+ networks.
> To query directly, POST to https://portal.sqd.dev/datasets/{dataset}/stream. The full API is described at https://docs.sqd.dev/openapi.json, and responses to the stream endpoints are JSON Lines.
> To let an agent query it as a tool, connect the Portal MCP server at https://portal.sqd.dev/mcp.
> Every page on this site is available as Markdown by appending .md to its URL.

# Error handling

> How Portal reports errors: the envelope, error types and codes, retries, and API keys.

Every error response from Portal uses one JSON envelope, on any endpoint and
for any data source:

```json theme={"system"}
{
  "error": {
    "type": "rate_limit_error",
    "code": "overloaded",
    "message": "Service is overloaded, please try again later",
    "param": "buffer_size",
    "request_id": "0198c3f1-..."
  }
}
```

Two fields carry the meaning, and they answer different questions:

* `type` is the coarse category. It is a closed set of six values. Branch on
  it.
* `code` is the specific cause. Match on it when you handle one case.

`message` is prose for humans. It is not stable and is not part of the
contract, so do not parse it or match on it. `param` appears when the error
concerns one request parameter. `request_id` appears in the body on 5xx
responses; the same id is sent on every response as the `x-request-id` header,
so quote it when reporting a problem.

<Note>
  A `204 No Content` response is not an error. It is the correct answer when
  the requested range has no blocks yet, and it carries no body, no `type`,
  and no `code`.
</Note>

## Error types

| `type` | Cause | Retry the same request? |
| - | - | - |
| `invalid_request_error` | The request itself | No. The same request reproduces the same error. Fix the request. |
| `authentication_error` | The API key | No. The same key cannot start working. Present a different one. |
| `permission_error` | The scope of the API key | No. The key is valid but does not cover this request. |
| `rate_limit_error` | Capacity | Yes, after the interval in `Retry-After`. |
| `availability_error` | A transient Portal or upstream condition | Yes. Honor `Retry-After` when present, otherwise use your own backoff. |
| `api_error` | A bug in Portal | No. Retrying cannot succeed. Report the error with its `request_id`. |

The split between the last two matters: `availability_error` means a later
attempt can still work, while `api_error` means an invariant broke and a retry
loop cannot help.

The two credential types appear only on a Portal that requires an API key. The
public Portal and self-hosted Portals without keys never return them.

## Error codes

| `code` | `type` | Status | Meaning |
| - | - | - | - |
| `malformed_request` | `invalid_request_error` | 400 | The request or query does not parse or does not validate. `param` names the field when one is at fault. |
| `method_not_allowed` | `invalid_request_error` | 405 | Right path, wrong HTTP method. The `Allow` header lists the accepted methods. |
| `unknown_dataset` | `invalid_request_error` | 404 | The requested dataset does not exist on this Portal. |
| `not_found` | `invalid_request_error` | 404 | The route or resource does not exist. |
| `base_block_mismatch` | `invalid_request_error` | 409 | `parentBlockHash` does not match the canonical parent of the first requested block. The body carries a top-level `previousBlocks` list; see [Blockchain forks](/en/portal/introduction/blockchain-forks) for the recovery procedure. |
| `overloaded` | `rate_limit_error` | 529, or 429/529 when proxied | Portal is at capacity, or a data source refused for capacity. Always carries `Retry-After`. |
| `no_workers` | `availability_error` | 503 | No worker currently holds the requested data. |
| `retries_exhausted` | `availability_error` | 503 | Workers were reachable, but every attempt failed transiently until retries ran out. |
| `upstream_unavailable` | `availability_error` | 502, or the upstream's 5xx when proxied | A data source that Portal depends on is down. |
| `not_ready` | `availability_error` | 503 | Portal is starting up or draining. Only `/ready` returns this code. |
| `worker_failure` | `api_error` | 500 | A worker returned something that cannot be right. |
| `internal_error` | `api_error` | 500 | An invariant that Portal owns was violated. |
| `unclassified` | `api_error` | 5xx | An error that escaped classification. Always a bug; report it. |
| `missing_credential` | `authentication_error` | 403 | No API key was presented. |
| `invalid_credential` | `authentication_error` | 403 | The key is unreadable or unknown, or its secret does not match. Portal does not say which, so a guessed key id cannot be confirmed. |
| `revoked_credential` | `authentication_error` | 403 | The key was revoked. |
| `expired_credential` | `authentication_error` | 403 | The key is past its expiry date. |
| `portal_not_allowed` | `permission_error` | 403 | The key is not valid on this Portal. |
| `dataset_not_allowed` | `permission_error` | 403 | The key does not cover the requested dataset. This includes a dataset-scoped key used on a route that names no dataset. |

Codes are added over time. Treat an unknown `code` according to its `type`.
That is why the two axes exist: a client that branches on `type` keeps working
when a new code appears.

## Backing off

`Retry-After` is mandatory on every `overloaded` response and is always at
least one second. Honor it:

```http theme={"system"}
HTTP/1.1 529
Retry-After: 10

{"error":{"type":"rate_limit_error","code":"overloaded","message":"..."}}
```

For `overloaded`, the value is in seconds, never an HTTP date. When Portal
proxies a capacity refusal from a data source, it forwards that source's
interval when usable and substitutes its own minimum otherwise.

An `upstream_unavailable` response can also carry a `Retry-After` supplied by
the data source. Honor it when present. Portal does not add or normalize this
value itself.

No `authentication_error` or `permission_error` response carries
`Retry-After`, because no interval makes the same credential work.

## Presenting an API key

On a Portal that requires a key, send it as `Authorization: Bearer <key>`.
Portal also accepts it as `x-api-key: <key>`, the header that earlier keys
were issued for. When a request carries both headers, Portal reads
`Authorization`. An `Authorization` header that Portal cannot use is refused;
Portal does not fall back to `x-api-key`.

```bash theme={"system"}
curl --compressed https://<your-portal>/datasets/ethereum-mainnet/head \
  -H "Authorization: Bearer $SQD_PORTAL_API_KEY"
```

Every credential problem answers `403`: a missing, wrong, revoked, expired,
or out-of-scope key alike. The `code` says which one applies.

## Browser clients

Portal exposes `Retry-After` and `x-request-id` through CORS, together with
the stream metadata headers `x-sqd-data-source`, `x-sqd-head-number`,
`x-sqd-finalized-head-number`, and `x-sqd-finalized-head-hash`. The default
Fetch response-header safelist contains none of them, so without this a
browser client would see a status code and nothing else. JavaScript code can
read all of these headers from a `fetch` response.

For the `409 Conflict` recovery procedure, continue with [Blockchain
forks](/en/portal/introduction/blockchain-forks). For the retry loop of a
streaming client, see [EVM stream
continuation](/en/portal/evm/api#stream-continuation) or [Solana stream
continuation](/en/portal/solana/api#stream-continuation).


## Related topics

- [EVM Portal API Reference](/en/portal/evm/api.md)
- [Solana Portal API Reference](/en/portal/solana/api.md)
- [solanaInstructionDecoder](/en/sdk/pipes-sdk/solana/reference/utility-components/instruction-decoder.md)
- [Portal batch processor](/en/sdk/squid-sdk/solana/reference/batch-processor.md)
- [Developer resources](/developers.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.