Summary
When testing_commitBlockV1 is called more than once against the same node, the
first call succeeds but every subsequent call fails, because committing a
block does not persist that block's state to the state backend. The next commit then
cannot scope the parent (just-committed) block's state.
- Flat backend →
InvalidOperationException: Unable to gather snapshots for state StateId { BlockNumber = N, ... }
- Pruning-trie (patricia) backend →
MissingTrieNodeException: Node ... is missing from the DB
This breaks cross-client conformance for the execution-apis testing_commitBlockV1
fixtures: go-ethereum passes all of them; Nethermind fails every case whose commit
runs after a prior commit on the same node.
Affected version
Where it comes from
#11593 changed testing_commitBlockV1 to produce the block with DoNotUpdateHead
and then commit via CommitAsMainChain (SuggestBlock(ForceDontSetAsMain) +
UpdateMainChain(..., wereProcessed: true)), bypassing the main BlockchainProcessor.
The state computed during the producer pass is therefore never persisted by the normal
processing path. The in-code comment acknowledges this is fragile:
// Must NOT set ReadOnlyChain — empirically, the producer pass under FlatDb
// only appends a snapshot bundle when the chain is not read-only; without
// it the next commit's BeginScope(parent) fails with "Unable to gather snapshots".
In practice the snapshot bundle for the committed block is still not available to the
next commit, so the symptom reproduces even with the flat backend correctly enabled.
Reproduction
Run the execution-apis rpc-compat fixtures for testing_commitBlockV1 against
Nethermind (hive). The fixtures issue one testing_commitBlockV1 each, in sequence,
against a shared node. Result: the first commit succeeds; all later commits fail.
Observed across three independent state-backend configurations (so it is not a
config issue):
| State backend config |
Node starts? |
Result |
Pruning trie, Pruning.Mode=None (archive) |
yes |
MissingTrieNodeException on commit #2+ |
Flat (FlatDb.Enabled=true, ImportFromPruningTrieState=true) |
yes |
Unable to gather snapshots on commit #2+ |
Flat (FlatDb.Enabled=true, fresh node) |
yes |
Unable to gather snapshots on commit #2+ |
(Note: the hive config key Init.UseMemDb cannot be set to false to get RocksDB —
its setter ignores the value and always sets DiagnosticMode = MemDb. Removing the
key is required to reach a RocksDB/flat backend at all.)
Failing request (commit #2, building on the previously committed head)
{"jsonrpc":"2.0","id":2,"method":"testing_commitBlockV1","params":[
{"parentBeaconBlockRoot":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884365149a42212e8822",
"prevRandao":"0x1111111111111111111111111111111111111111111111111111111111111111",
"suggestedFeeRecipient":"0x0000000000000000000000000000000000000000",
"timestamp":"0x1e6","withdrawals":[]},
["0x02f8..."], "0x746573745f6e616d65"]}
Response (flat backend)
-32603 Internal error
System.InvalidOperationException: Unable to gather snapshots for state
StateId { BlockNumber = 46, StateRoot = 0x4c6709fca84ff2bcca8609b4a54cb96fc1c001862d450d334088cd5694cad374 }.
at Nethermind.State.Flat.FlatDbManager.GatherReadOnlySnapshotBundle(StateId& baseBlock) FlatDbManager.cs:295
at Nethermind.State.Flat.FlatDbManager.GatherSnapshotBundle(StateId& baseBlock, Usage usage) FlatDbManager.cs:236
at Nethermind.State.Flat.ScopeProvider.FlatScopeProvider.BeginScope(BlockHeader baseBlock) FlatScopeProvider.cs:32
at Nethermind.State.WorldState.BeginScope(BlockHeader baseBlock) WorldState.cs:234
at Nethermind.Consensus.Processing.BranchProcessor.Process(...) BranchProcessor.cs:70
at Nethermind.Consensus.Processing.BlockchainProcessor.Process(...) BlockchainProcessor.cs:437
at Nethermind.Consensus.Processing.OneTimeChainProcessor.Process(...) OneTimeChainProcessor.cs:31
at Nethermind.Merge.Plugin.TestingRpcModule.ProduceBlock(...) TestingRpcModule.cs:145
at Nethermind.Merge.Plugin.TestingRpcModule.testing_commitBlockV1(...) TestingRpcModule.cs:97
Response (pruning-trie backend)
-32000 Node A: P: H:0x4c6709fca84ff2bcca8609b4a54cb96fc1c001862d450d334088cd5694cad374 is missing from the DB
Nethermind.Trie.MissingTrieNodeException ...
at Nethermind.Blockchain.BeaconBlockRoot.BeaconBlockRootHandler.BeaconRootsAccessList(...)
at Nethermind.Merge.Plugin.TestingRpcModule.ProduceBlock(...) TestingRpcModule.cs:145
In both cases the state being requested is the head produced by the previous
testing_commitBlockV1, confirming that commit did not persist its post-state.
Expected behavior
After testing_commitBlockV1 commits a block and advances the head, that block's
state must be persisted so a subsequent testing_commitBlockV1 (or any block
production) can scope it as the parent. go-ethereum does this and passes all
execution-apis testing_commitBlockV1 fixtures.
Suggested fix area
Nethermind.Merge.Plugin.TestingRpcModule (commit path / CommitAsMainChain) and
Nethermind.State.Flat.FlatDbManager — persist the committed block's snapshot bundle
(and the patricia state) on commit, rather than relying on the producer pass having
done so.
References
Summary
When
testing_commitBlockV1is called more than once against the same node, thefirst call succeeds but every subsequent call fails, because committing a
block does not persist that block's state to the state backend. The next commit then
cannot scope the parent (just-committed) block's state.
InvalidOperationException: Unable to gather snapshots for state StateId { BlockNumber = N, ... }MissingTrieNodeException: Node ... is missing from the DBThis breaks cross-client conformance for the execution-apis
testing_commitBlockV1fixtures: go-ethereum passes all of them; Nethermind fails every case whose commit
runs after a prior commit on the same node.
Affected version
1.39.0-unstable+afddfd76(nethermindeth/nethermind:master, pulled 2026-06-12)testing_commitBlockV1")Where it comes from
#11593 changed
testing_commitBlockV1to produce the block withDoNotUpdateHeadand then commit via
CommitAsMainChain(SuggestBlock(ForceDontSetAsMain)+UpdateMainChain(..., wereProcessed: true)), bypassing the main BlockchainProcessor.The state computed during the producer pass is therefore never persisted by the normal
processing path. The in-code comment acknowledges this is fragile:
In practice the snapshot bundle for the committed block is still not available to the
next commit, so the symptom reproduces even with the flat backend correctly enabled.
Reproduction
Run the execution-apis
rpc-compatfixtures fortesting_commitBlockV1againstNethermind (hive). The fixtures issue one
testing_commitBlockV1each, in sequence,against a shared node. Result: the first commit succeeds; all later commits fail.
Observed across three independent state-backend configurations (so it is not a
config issue):
Pruning.Mode=None(archive)MissingTrieNodeExceptionon commit #2+FlatDb.Enabled=true,ImportFromPruningTrieState=true)Unable to gather snapshotson commit #2+FlatDb.Enabled=true, fresh node)Unable to gather snapshotson commit #2+(Note: the hive config key
Init.UseMemDbcannot be set tofalseto get RocksDB —its setter ignores the value and always sets
DiagnosticMode = MemDb. Removing thekey is required to reach a RocksDB/flat backend at all.)
Failing request (commit #2, building on the previously committed head)
{"jsonrpc":"2.0","id":2,"method":"testing_commitBlockV1","params":[ {"parentBeaconBlockRoot":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884365149a42212e8822", "prevRandao":"0x1111111111111111111111111111111111111111111111111111111111111111", "suggestedFeeRecipient":"0x0000000000000000000000000000000000000000", "timestamp":"0x1e6","withdrawals":[]}, ["0x02f8..."], "0x746573745f6e616d65"]}Response (flat backend)
Response (pruning-trie backend)
In both cases the state being requested is the head produced by the previous
testing_commitBlockV1, confirming that commit did not persist its post-state.Expected behavior
After
testing_commitBlockV1commits a block and advances the head, that block'sstate must be persisted so a subsequent
testing_commitBlockV1(or any blockproduction) can scope it as the parent. go-ethereum does this and passes all
execution-apis
testing_commitBlockV1fixtures.Suggested fix area
Nethermind.Merge.Plugin.TestingRpcModule(commit path /CommitAsMainChain) andNethermind.State.Flat.FlatDbManager— persist the committed block's snapshot bundle(and the patricia state) on commit, rather than relying on the producer pass having
done so.
References
HIVE_TARGET_GAS_LIMIT): clients: enable testing namespace and pin gas-limit target ethereum/hive#1496