How to read a liteserver error

A liteserver answers every query with either the requested object or a liteServer.error carrying an integer code and a message. The TONNode gateway uses the same envelope for its own refusals, so your library surfaces them the way it surfaces any node error: tonutils-go keeps the code in ton.LSError, pytoniq raises LiteServerError with .code and .message, and ton-lite-client throws an Error whose message is the text alone.

The codes come from two places, and the table marks which:

  • Gateway codes — 400, 403, 429, 500, 502 — are produced by the TONNode gateway that sits between your ADNL connection and the node. It checks the method against your plan, applies the rate limit and the concurrency cap, and routes the query to a light node or an archive node. These rows restate Chapter 5 of the LiteServers documentation.
  • Node codes come from the node software in ton-blockchain/ton. -400 is the default code a LiteQuery failure text is sent with, -503 timeout is the node's own per-query deadline, and 651 and 652 are ErrorCode::notready and ErrorCode::timeout from common/errorcode.h. Two rows are raised by the C++ client itself (lite-client, tonlib) rather than by the server; other languages raise their own exceptions in those situations.

The ones that matter most

403 archive history is not included in this plan. The one signal that matters when you walk history. The query needed a block the light node no longer holds — the light node keeps about two days of history at the current block rate, an order of magnitude rather than a promise — and the plan has no archive scope. It is not retryable: either add archive to the plan or ask for a more recent block. Chapter 4 explains where the boundary is and includes a probe that measures it for your key.

429, three different limits. too many requests is the per-key rate limit, a leaky bucket denominated in cost units: an ordinary read costs 5, a full getTransactions page up to 16, a whole block 50. too many concurrent requests for this key is the in-flight cap — half the plan's RPS, floored at 8 and capped at 128. gateway is saturated is the global ceiling of 256 in-flight queries across all customers. The first two mean your client needs pacing; the last means the backend is struggling, and the right response is backoff. Chapter 7 has the cost table and paced-client snippets.

502 backend node timeout. The node did not answer inside the gateway's window: 7 s for the light node, 20 s for archive. Set your own client timeout above those numbers; a shorter one turns a real 502 into a local timeout and loses the diagnosis.

651 and the "not in db" family. block … is not applied, block … is not in db and lt not in db all carry ErrorCode::notready. They mean this node does not have what you asked for ready: it is still catching up, the block is ahead of its state, or the block or logical time is older than its storage. A fresh block will appear on its own; an old one needs archive.

What to retry

Retry 429 with backoff and 502 once, immediately. Do not retry 403 — neither variant becomes true on a second attempt. Do not retry -400 from a transaction walk either; it is a boundary, not a blip. For 651, retry a block that is newer than the node's state after a moment, and stop retrying one that is older than its storage.

Mempool stream errors

The mempool stream is a different protocol with its own error list — JSON error messages and WebSocket close codes such as 4401 and 4429 — and is not covered by this table.