What happened
geth #35588 added checks to eth_call and friends that reject an accessList before Berlin, blobVersionedHashes before Cancun and an authorizationList before Prague. Erigon ported it in erigontech/erigon#23700 and their mainnet test suite broke on 7 pre-Berlin eth_createAccessList fixtures. That raised the question of what the spec expects. Right now it says nothing.
What clients do today
Measured with hive rpc-compat on the shared test chain at block 20 (pre-Berlin, pre-Cancun, pre-Prague). ethrex has no state before London on that chain, so its numbers are from block 0. Images: ethpandaops/* master/main as of 2026-09-08, plus the Erigon PR branch built from source.
eth_call with a caller-supplied field:
| Client |
accessList pre-Berlin |
blobVersionedHashes pre-Cancun |
authorizationList pre-Prague |
| geth master |
rejects, -32000 |
rejects, -32000 |
rejects, -32000 |
| reth |
rejects, -32003 |
rejects, -32003 |
rejects, -32003 |
| Besu |
rejects, -32602 |
rejects, -32602 |
rejects, -32602 |
| Nethermind |
rejects, -32603 "Internal error" |
rejects, -32603 "Internal error" |
rejects, -32603 "Internal error" |
| Erigon main |
accepts |
accepts |
rejects, -32000 |
| Erigon #23700 |
rejects, -32000 |
rejects, -32000 |
rejects, -32000 |
| ethrex |
accepts |
accepts unless maxFeePerBlobGas is set, then -32015 |
rejects, -32015 |
An empty accessList: [] behaves the same as a populated one everywhere.
eth_createAccessList pre-Berlin with no accessList in the request:
| Client |
plain transfer |
contract call that touches storage |
| geth master |
fails, -32000 |
fails, -32000 |
| reth |
fails, -32003 |
fails, -32003 |
| Besu |
{"accessList":[],"gasUsed":"0x5208"} |
success body with accessList: [], error: "execution reverted", gasUsed: 0x2faf081 (gas cap + 1) |
| Nethermind |
{"accessList":[],"gasUsed":"0x5208"} |
{"accessList":[],"gasUsed":"0xbcf4"} |
| Erigon main and #23700 |
{"accessList":[],"gasUsed":"0x5208"} |
one entry, two storage keys, gasUsed: 0xd52c |
| ethrex |
{"accessList":[],"gasUsed":"0x5208"} |
one entry, two storage keys, gasUsed: 0xbf4c |
Post-Berlin controls at latest pass on every client with identical results, so the divergence is the fork boundary, not the fixtures.
Proposal
One rule, no special cases. A simulation request that carries a field whose EIP is not active at the target block is invalid and the client rejects it. This applies to eth_call, eth_estimateGas, eth_createAccessList, eth_simulateV1 and debug_traceCall, and to accessList, blobVersionedHashes and authorizationList. It also applies to eth_createAccessList itself on a block where EIP-2930 is not active, since the method has nothing meaningful to return there.
This matches what four of six clients already do for eth_call, and what Erigon does after #23700. Changes needed: ethrex adds the gate, Nethermind turns its internal error into a normal rejection, Besu stops reporting the eth_createAccessList failure as a success, Erigon drops the exception in #23700 and updates its fixtures.
The alternative is to require eth_createAccessList to succeed regardless of fork. That needs fixes in geth, reth and Besu and still leaves three clients disagreeing on the result, so I am not proposing it.
Error codes are out of scope here. They differ across clients today and belong in #784.
Testing
The shared chain already covers this: Berlin activates at block 24, Cancun at 42, Prague at 45. Once the wording lands I will add fixtures targeting block 20 for the three fields and for eth_createAccessList.
Related: #853 and #854 cover a different eth_createAccessList problem, fee defaults, and stay separate.
What happened
geth #35588 added checks to
eth_calland friends that reject anaccessListbefore Berlin,blobVersionedHashesbefore Cancun and anauthorizationListbefore Prague. Erigon ported it in erigontech/erigon#23700 and their mainnet test suite broke on 7 pre-Berlineth_createAccessListfixtures. That raised the question of what the spec expects. Right now it says nothing.What clients do today
Measured with hive rpc-compat on the shared test chain at block 20 (pre-Berlin, pre-Cancun, pre-Prague). ethrex has no state before London on that chain, so its numbers are from block 0. Images:
ethpandaops/*master/main as of 2026-09-08, plus the Erigon PR branch built from source.eth_callwith a caller-supplied field:accessListpre-BerlinblobVersionedHashespre-CancunauthorizationListpre-PraguemaxFeePerBlobGasis set, then -32015An empty
accessList: []behaves the same as a populated one everywhere.eth_createAccessListpre-Berlin with noaccessListin the request:{"accessList":[],"gasUsed":"0x5208"}accessList: [],error: "execution reverted",gasUsed: 0x2faf081(gas cap + 1){"accessList":[],"gasUsed":"0x5208"}{"accessList":[],"gasUsed":"0xbcf4"}{"accessList":[],"gasUsed":"0x5208"}gasUsed: 0xd52c{"accessList":[],"gasUsed":"0x5208"}gasUsed: 0xbf4cPost-Berlin controls at
latestpass on every client with identical results, so the divergence is the fork boundary, not the fixtures.Proposal
One rule, no special cases. A simulation request that carries a field whose EIP is not active at the target block is invalid and the client rejects it. This applies to
eth_call,eth_estimateGas,eth_createAccessList,eth_simulateV1anddebug_traceCall, and toaccessList,blobVersionedHashesandauthorizationList. It also applies toeth_createAccessListitself on a block where EIP-2930 is not active, since the method has nothing meaningful to return there.This matches what four of six clients already do for
eth_call, and what Erigon does after #23700. Changes needed: ethrex adds the gate, Nethermind turns its internal error into a normal rejection, Besu stops reporting theeth_createAccessListfailure as a success, Erigon drops the exception in #23700 and updates its fixtures.The alternative is to require
eth_createAccessListto succeed regardless of fork. That needs fixes in geth, reth and Besu and still leaves three clients disagreeing on the result, so I am not proposing it.Error codes are out of scope here. They differ across clients today and belong in #784.
Testing
The shared chain already covers this: Berlin activates at block 24, Cancun at 42, Prague at 45. Once the wording lands I will add fixtures targeting block 20 for the three fields and for
eth_createAccessList.Related: #853 and #854 cover a different
eth_createAccessListproblem, fee defaults, and stay separate.