Why newHeads repeats a block number

Last updated:

At a glance

Symptom details
PropertyValue
During a reorgnewHeads can emit multiple headers at the same height
While catching upOnly the last of several simultaneous blocks is emitted
Logs on a reorgResent with removed: true
Safe key for blocksBlock hash, not block number

What you see

A newHeads listener receives block 1000, then another block 1000 with a different hash, or jumps from 1000 to 1003. A logs subscription delivers a log you already processed, now marked removed.

Why this happens

Geth's pub/sub documentation says: "In case of a chain reorganization the subscription will emit the last header in the new chain. Therefore the subscription can emit multiple headers on the same height." It also notes that if the node receives multiple blocks simultaneously, for example when catching up, "only the last block is emitted". For logs, "in case of a chain reorganization previous sent logs that are on the old chain will be resent with the removed property set to true".

What to do

  1. Key blocks by hash; when a header arrives at a height you already have, replace the old block and everything built on it.
  2. When the block number jumps, fetch the missing blocks with eth_getBlockByNumber.
  3. Undo the effect of any log that arrives with removed true.
  4. Act on data only after the confirmation level your application needs (for example the finalized block).

When this is not our problem

Reorgs come from the chain's consensus, and the way subscriptions report them is part of the node's pub/sub API — the same whoever operates the node, Triport included.

FAQ

Why did newHeads send the same block number twice?
A chain reorganization replaced that block; the subscription emits the header of the new chain, so the height repeats with a different hash.
Why does newHeads skip block numbers?
When the node receives several blocks at once, only the last one is emitted. Fetch the missing blocks by number.

Sources