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

# Errors and retries

> Which errors to retry, how, and how to handle failures in the middle of a response.

## The error format

Errors returned before the response starts are a non-`200` status with a JSON body:

```json theme={null}
{"error": {"code": "invalid_query", "message": "fromBlock (10) is greater than toBlock (9)"}}
```

Branch on `code`, not on the message. Messages are for people and can change. The full list is in
[Errors](https://docs.bloklake.com/api-reference/errors.md).

## What to retry

| Status | Codes | Retry? |
| - | - | - |
| `400` | `invalid_query` | **No.** Fix the request. |
| `400` | `unknown_query_id` | **Yes, differently**: resend the full filter instead of the id. |
| `401` | `missing_api_key`, `invalid_api_key`, `key_revoked` | **No.** Check the key. |
| `402` | `balance_exhausted`, `overage_limit_reached` | **No.** A person must top up or raise the limit. |
| `403` | `key_suspended` | **No.** Fix billing. |
| `403` | `quota_exceeded` | **No.** Delete unused sets. |
| `404` | `unknown_set` | **Yes, differently**: create the set again, then retry. |
| `429` | `rate_limited`, `too_many_in_flight`, `set_rate_limited`, `key_blocked`, `ip_rate_limited`, `ip_blocked` | **Yes**, after `Retry-After`. |
| `503` | `unavailable`, `auth_unavailable` | **Yes**, after `Retry-After`, with backoff. |
| `500` | `internal` | **Yes**, a few times with backoff; report it if it persists. |
| — | connection reset, timeout, truncated body | **Yes**, with backoff. |

Queries are read-only and deterministic for finalized blocks, so retrying is always safe.

## Backoff

Use `Retry-After` when it's present. Otherwise use exponential backoff with a cap and some jitter:
1 s, 2 s, 4 s, 8 s, … up to 30 s, giving up after a handful of attempts. The
[client libraries](https://docs.bloklake.com/clients.md) do exactly this, as does the [Arrow reader](https://docs.bloklake.com/responses/reading-arrow.md#complete-readers).

## Failures in the middle of a response

Validation happens before any data is sent, so almost every error arrives as a clean status and
JSON body. But if something fails **after** the server has started streaming (a server restart,
a network problem), it can't send an error any more: the connection is closed.

Your client must treat that as a failed request, not a short result:

* an I/O error or a missing end-of-stream marker while reading the Arrow body, or
* fewer rows received than `bl.objects` says.

Then discard what you received and retry the request. See
[Reading Arrow](https://docs.bloklake.com/responses/reading-arrow.md) for readers that check this.

> [!WARNING]
>
> Never treat a partially read response as complete. The worst failure mode is silently missing
> rows, which you only find much later.

## Slow and idle clients

Each response holds one of the chain's query slots until it has been read. To keep slots free,
the server cuts off:

* a response the client reads **nothing** of for **60 seconds**, and
* a response the client keeps reading at **under 50 KB/s**, measured over 30 seconds of waiting
  on the client (time the server spends finding rows doesn't count).

If you process rows slowly, read the whole response into memory first, or use smaller pages
(`maxObjects`), then do the slow work.

## Timeouts

A single page usually completes in well under a second; a page of 100,000 rows over a slow link
takes a few seconds. Set a connect timeout of about 10 s and a read timeout of 60–120 s.

## Request ids

Send your own `X-Request-Id` (up to 128 printable characters), or read the one the server
generated from the response headers. Log it with every error and include it when you contact
support.


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