面向开发者 / JSON-RPC

错误与限制

五个错误码承载了一切。值得好好处理的是 -32005,因为它表示一个限制而不是一个错误:同样的请求稍后就会成功。

五个错误码

错误码Value
-32602Invalid paramsa malformed address, block tag, filter id or topic filter
-32601Method not found or not availablean unregistered name, or one refused by design such as eth_sendTransaction
-32000Executiona revert, a decode failure, a signature failure, or a consensus rejection
-32005Limita rate limit, a full queue, a timeout, a size cap or a filter cap - every row on this page
-32603Internala write-ahead-log or coordinator failure; also every non-send error when you are on the WebSocket transport

在运维上真正要紧的区别是 -32000 和 -32005 之间的那一个。一个执行 错误是关于你这笔交易的事实,重试改变不了任何东西;一个限制错误是关于节点当前负载的事实, 等到有空位时同样的请求就会成功。

没有 error.data

你的库无法解码 revert 原因

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.

差异那一页写了变通办法。

Sending a transaction

限制值变量越界时
Raw transaction sizechecked on the hex string BEFORE decoding, so an oversized transaction is a limit error rather than a decode error128 KiB编译期-32005
Admission queuequeued transactions across all senders4,096 totalADMISSION_CAPACITY-32005 "sequencer admission queue is full"
Per sendercompile-time, so one sender cannot fill the queue with unfillable nonces64编译期-32005
Execution waitthe coordinator's own deadline is 5 s; the call waits until the transaction executes or this expires6 s编译期-32005 "transaction timed out waiting for execution"
Per execution batchMAX_ADMIT_PER_BATCH - a throughput mechanism, not a caller-visible limit512编译期-

Expensive reads

限制值变量越界时
Token bucket burstguards eth_call, eth_estimateGas and eth_getLogs together256HEAVY_READ_BURST-32005 "rate limit exceeded for expensive read methods"
Refill rateprocess-wide and shared by every caller - explicitly a backstop, not per-client limiting128/sHEAVY_READ_PER_SEC-
eth_getLogs spana span of 5,000 or more is refused outrightunder 5,000 blocks编译期-32005 "log block range is too wide"
eth_getLogs matchesnarrow the filter or walk the range in windows10,000编译期-32005 "eth_getLogs matched too many logs"

The HTTP listener

限制值变量越界时
Connections512RPC_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 calls2 MiBRPC_MAX_REQUEST_BYTES-
Response body16 MiBRPC_MAX_RESPONSE_BYTES-
Batch lengthsplit a larger batch client-side32RPC_MAX_BATCH-
Keep-aliveTCP_NODELAY is on30 sRPC_KEEP_ALIVE_SECS-

Filters

限制值变量越界时
Filters per node1,024MAX_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 end1,024 entriesMAX_FILTER_BUFFER-
Idle expirymeasured since the last poll, and expiry is silent - the next poll answers -32602 "filter not found"300 sFILTER_TTL_SECS-32602

两个不是配置的值

MIN_GAS_PRICE (1 wei) 和 EXECUTION_SPEC (Cancun) 看起来像设置项,而它们是刻意做成编译期常量的。

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.

把限制处理好

这里没有按客户端计的限流,也没有剩余预算响应头,所以客户端没法从一个信号里自我节流,它必须 在构造上就守规矩。有三件事要紧:

  • 在 -32005 上退避,不要立刻重试。重读令牌桶是所有调用者共享的,所以一个紧凑的重试循环是让它对包括你自己在内的所有人都保持 空桶的最快方式。
  • 把你的 eth_getLogs 范围切成窗口,控制在 5,000 个区块以下,并预期 10,000 条的匹配上限。在一条大约保留两分钟区块的链上, 你最先撞上的实际限制是保留期而不是跨度。
  • 把批量请求控制在 32 个以内。超过之后整个批量会被拒绝,而不是被截断。
javascript
// RPC_URL: https://rpc.picklechain.xyz on the public testnet, or a node of your own.
async function call(body, attempt = 0) {
  const res = await fetch(RPC_URL, {
    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;
}