Summary
A single invalid transaction in the mempool causes the REVM executor to return a fatal error during block building. The ? operator in the executor loop propagates the error, aborting the entire block — not just the offending transaction. Because mempool pruning only happens on finalization, and no block is produced, the bad transaction is never removed. Every subsequent leader hits the same failure, creating a permanent chain stall.
Load Test Evidence (2026-05-22)
A load test of 1,000 transactions (10 accounts, 50 concurrency) against a fresh 4-validator devnet confirmed this:
- Results: 865/1000 success (86.5%), 135 failures, 8.09 TPS
- Block builder failure loop: 1,373
build_block failures in a single minute on one validator:
WARN build_block: execution failed height=288 txs=195 error=TxExecution("Transaction(NonceTooHigh { tx: 24, state: 0 })")
- The block builder includes all ~195 pool txs in nonce-unaware order. When a tx with nonce 24 is encountered before nonces 0-23 are finalized,
evm.replay() returns NonceTooHigh and the ? aborts the entire block.
Root Cause
In crates/node/executor/src/revm.rs, lines 391 and 395, two ? operators abort the entire execute() call on any individual transaction failure:
let tx_env = decode_tx_env(tx_bytes, self.config.chain_id)?; // aborts on decode failure
let result_and_state = evm.replay().map_err(...)?; // aborts on execution failure
Proposed Fix
Replace ? with match + continue to skip individual bad transactions. Track skipped txs in ExecutionOutcome::skipped_txs and evict them from the mempool in build_block().
See tmp/issues/01-executor-fatal-abort-on-invalid-transaction.md for the full writeup with code samples.
Summary
A single invalid transaction in the mempool causes the REVM executor to return a fatal error during block building. The
?operator in the executor loop propagates the error, aborting the entire block — not just the offending transaction. Because mempool pruning only happens on finalization, and no block is produced, the bad transaction is never removed. Every subsequent leader hits the same failure, creating a permanent chain stall.Load Test Evidence (2026-05-22)
A load test of 1,000 transactions (10 accounts, 50 concurrency) against a fresh 4-validator devnet confirmed this:
build_blockfailures in a single minute on one validator:evm.replay()returnsNonceTooHighand the?aborts the entire block.Root Cause
In
crates/node/executor/src/revm.rs, lines 391 and 395, two?operators abort the entireexecute()call on any individual transaction failure:Proposed Fix
Replace
?withmatch+continueto skip individual bad transactions. Track skipped txs inExecutionOutcome::skipped_txsand evict them from the mempool inbuild_block().See
tmp/issues/01-executor-fatal-abort-on-invalid-transaction.mdfor the full writeup with code samples.