xOneRS limits eth_getLogs in two ways: a query may span at most 5,000 blocks, and it may return at most 10,000 logs. Over either limit, the call fails with an error and no partial list. On builds from 2026-11-14 on, the overflow error also tells the client which smaller range is safe to ask for. Since PR #1074 (2026-11-15) both limits are operator settings: --rpc.rangelimit and --rpc.logs.maxresults, as geth and erigon offer. Since PR #1106 a persistent log index narrows the blocks a query reads.

What are the limits?

From crates/xone-rpc/src/server.rs and eth.rs (source) and PR #1074:

REFERENCE TABLEScroll to explore all columns →
Limit Default Setting Error
Block range (toBlock - fromBlock) less than 5,000, so at most 5,000 blocks --rpc.rangelimit, or rpc_range_limit in [limits] -32602 exceed maximum block range: 5000
Logs in one answer 10,000 --rpc.logs.maxresults, or rpc_logs_max_results in [limits] -32000 query returned more than 10000 results

0 means no limit. The values appear in dumpconfig and in the RPC start log. The error texts name the configured value.

trace_filter uses the same 5,000-block bound. eth_getLogs is in cost class C2 (20 units, 10 s deadline) of the RPC quota (docs/rpc/methods.md). A call refused by the quota gets -32005 limit exceeded.

What did a real node answer?

We sent three queries without an address or topic filter to an xOneRS net2040 archive node (build fa3edb8e, 2026-11-14) on 2026-11-15. One call each, read-only:

REFERENCE TABLEScroll to explore all columns →
Query Time Answer
The last 5,000 blocks 514 ms 4,642 logs
The last 5,001 blocks 5 ms exceed maximum block range: 5000
Blocks 40,000 to 44,999 (a load-test range) 4,331 ms query returned more than 10000 results

These are single calls on one host, not a benchmark. The refusal over the range limit comes before any block is read, so it is fast. The result limit is found during the scan, so the node read blocks until it passed 10,000 logs.

How does the client know what to retry?

Since PR #882 (2026-11-14, issue #848), the overflow error carries data:

  • retryFrom and retryTo: a range that was scanned completely and stayed within the limit. Ask for it again and it will fit.
  • nextFrom: where the rest starts.
  • unsplittable: true, with block if known: the first block alone has more than 10,000 logs, so no smaller block range helps. Add an address or topic filter.
  • limit: the limit that was passed.

The build we queried is older than #882, so its error had no data. A client should handle both: use retryTo when it is there, and halve the range when it is not.

def get_logs(rpc, frm, to, flt):
    """rpc(method, params) returns the JSON-RPC response object."""
    out, end = [], min(to, frm + 4999)
    while frm <= to:
        r = rpc("eth_getLogs", [{**flt, "fromBlock": hex(frm), "toBlock": hex(end)}])
        if "result" in r:
            out += r["result"]
            frm = end + 1
            end = min(to, frm + 4999)
            continue
        d = r["error"].get("data") or {}
        if d.get("unsplittable"):
            raise RuntimeError(f"block {d.get('block')} alone has more than {d['limit']} logs")
        if "retryTo" in d:
            end = int(d["retryTo"], 16)        # current builds: a range that fits
        elif end > frm:
            end = frm + (end - frm) // 2       # older builds: no data, halve the range
        else:
            raise RuntimeError(r["error"]["message"])
    return out

After a reorg the hint is not valid. The node then fails the query with a different error, and the client should run the whole query again.

What other errors can a log query return?

  • <what> of canonical block N is not available; the log query is incomplete: the node could not read a block or its receipts. It refuses rather than return a list that looks complete.
  • unsupported history: ...: the range starts below the node's retained window. Ask an archive node. See archive vs full for RPC providers.
  • request cancelled: the node cancelled the request during the scan.

The node checks each block's bloom filter first and reads receipts only for blocks that may match. Since PR #1106 it also keeps a persistent index from address and topic to block, written with each canonical commit: inside the range the index covers, a query loads only candidate blocks, and outside it the bloom path still applies. A filter on address or topics therefore makes a query cheaper.

How do the other clients handle this?

REFERENCE TABLEScroll to explore all columns →
Client Range limit Result limit Log index
xOneRS --rpc.rangelimit, default 5,000 --rpc.logs.maxresults, default 10,000 yes, persistent (#1106)
geth --rpc.rangelimit (v1.17.0) not covered here not covered here
erigon --rpc.blockrange.limit --rpc.logs.maxresults (erigon 3.4) not covered here
nethermind not covered here not covered here --LogIndex.Enabled true; a query of more than 15 minutes went to about 1 s, per Nethermind

The competitor columns are what those pages say. We did not test them.

Frequently asked questions

What is the maximum block range for eth_getLogs on xOne?

5,000 blocks. A larger range answers -32602 exceed maximum block range: 5000.

What happens when eth_getLogs matches more than 10,000 logs?

The call fails with query returned more than 10000 results. On current builds, data.retryFrom and data.retryTo name a range that fits.

Can I raise the limits on my own xOne node?

Yes, on builds from main after 2026-11-15: set --rpc.rangelimit and --rpc.logs.maxresults (0 is no limit). The v0.1.0 release has the fixed limits.

Why does a filter on address make eth_getLogs faster?

The log index and the bloom filters rule out blocks that cannot match, so the node reads fewer receipts.

Sources