For developers / JSON-RPC
Errors and limits
LiveFive error codes carry everything. -32005 is the one worth handling properly, because it means a limit rather than a mistake - the same request will succeed later.
The five codes
| Code | Value |
|---|---|
| -32602 | Invalid paramsa malformed address, block tag, filter id or topic filter |
| -32601 | Method not found or not availablean unregistered name, or one refused by design such as eth_sendTransaction |
| -32000 | Executiona revert, a decode failure, a signature failure, or a consensus rejection |
| -32005 | Limita rate limit, a full queue, a timeout, a size cap or a filter cap - every row on this page |
| -32603 | Internala write-ahead-log or coordinator failure; also every non-send error when you are on the WebSocket transport |
Read from sequencer/src/rpc.rs:40
The distinction that matters operationally is between -32000 and -32005. An execution error is a fact about your transaction and retrying changes nothing; a limit error is a fact about the node's current load and the identical request will succeed once there is room.
No error.data
Revert reasons cannot be decoded by your library
A revert arrives as message text - "execution reverted: 0x<returndata>" - and error.data is absent.
Libraries decode custom errors and revert strings out of error.data, so on this chain they cannot. ethers and viem will both surface the raw message instead of a decoded error. If you need the reason, parse the hex out of the message yourself and decode it against your own ABI.
The deviations page has the workaround.
Sending a transaction
| Limit | Value | Variable | On breach |
|---|---|---|---|
| Raw transaction sizechecked on the hex string BEFORE decoding, so an oversized transaction is a limit error rather than a decode error | 128 KiB | compile-time | -32005 |
| Admission queuequeued transactions across all senders | 4,096 total | ADMISSION_CAPACITY | -32005 "sequencer admission queue is full" |
| Per sendercompile-time, so one sender cannot fill the queue with unfillable nonces | 64 | compile-time | -32005 |
| Execution waitthe coordinator's own deadline is 5 s; the call waits until the transaction executes or this expires | 6 s | compile-time | -32005 "transaction timed out waiting for execution" |
| Per execution batchMAX_ADMIT_PER_BATCH - a throughput mechanism, not a caller-visible limit | 512 | compile-time | - |
Read from sequencer/src/pipeline.rs:19
Expensive reads
| Limit | Value | Variable | On breach |
|---|---|---|---|
| Token bucket burstguards eth_call, eth_estimateGas and eth_getLogs together | 256 | HEAVY_READ_BURST | -32005 "rate limit exceeded for expensive read methods" |
| Refill rateprocess-wide and shared by every caller - explicitly a backstop, not per-client limiting | 128/s | HEAVY_READ_PER_SEC | - |
| eth_getLogs spana span of 5,000 or more is refused outright | under 5,000 blocks | compile-time | -32005 "log block range is too wide" |
| eth_getLogs matchesnarrow the filter or walk the range in windows | 10,000 | compile-time | -32005 "eth_getLogs matched too many logs" |
Read from sequencer/src/rpc.rs:555
The HTTP listener
| Limit | Value | Variable | On breach |
|---|---|---|---|
| Connections | 512 | RPC_MAX_CONNECTIONS | - |
| Request bodythe public vhost caps a request at 1 MiB before this applies, and 1 MiB of JSON-RPC batch is already thousands of calls | 2 MiB | RPC_MAX_REQUEST_BYTES | - |
| Response body | 16 MiB | RPC_MAX_RESPONSE_BYTES | - |
| Batch lengthsplit a larger batch client-side | 32 | RPC_MAX_BATCH | - |
| Keep-aliveTCP_NODELAY is on | 30 s | RPC_KEEP_ALIVE_SECS | - |
Read from sequencer/src/rpc.rs:2286
Filters
| Limit | Value | Variable | On breach |
|---|---|---|---|
| Filters per node | 1,024 | MAX_FILTERS | -32005 "filter limit reached" |
| Buffer per filterthe OLDEST entries are trimmed when you stop polling, so a slow poller loses the beginning of its backlog rather than the end | 1,024 entries | MAX_FILTER_BUFFER | - |
| Idle expirymeasured since the last poll, and expiry is silent - the next poll answers -32602 "filter not found" | 300 s | FILTER_TTL_SECS | -32602 |
Read from sequencer/src/filters.rs:34
Two values that are not configuration
MIN_GAS_PRICE (1 wei) and EXECUTION_SPEC (Cancun) look like settings and are deliberately compile-time constants.
Both are compile-time constants rather than environment variables, and that is a correctness requirement rather than an oversight. Either one made operator-tunable would let a replica reject or re-price a transaction the leader had accepted, and derivation would halt on the divergence. Pinning the hardfork also means a revm upgrade cannot silently change gas accounting underneath an already-settled chain.
Read from sequencer/src/producer.rs:337
Handling limits well
There is no per-client rate limiting and no remaining-budget header, so a client cannot self-throttle from a signal - it has to be well behaved by construction. Three things matter:
- Back off on
-32005, do not retry immediately. The heavy-read bucket is shared across every caller, so a tight retry loop is the fastest way to keep it empty for everyone including yourself. - Window your
eth_getLogsranges below 5,000 blocks and expect the match cap at 10,000. On a chain that retains about two minutes of blocks, the practical limit you meet first is retention rather than span. - Keep batches to 32. Above that the batch is rejected rather than truncated.
async function call(body, attempt = 0) {
const res = await fetch("https://rpc.picklechain.xyz", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(body),
});
const json = await res.json();
// -32005 is a limit, not a mistake: the same request works later.
if (json.error?.code === -32005 && attempt < 5) {
await new Promise((r) => setTimeout(r, 2 ** attempt * 250));
return call(body, attempt + 1);
}
return json;
}