Why Ethereum Block Subscriptions Are Unreliable
December 26, 2024
If you're building an Ethereum application that needs real-time block data, you've probably considered using websocket subscriptions. But before you dive in, there's something you should know about their reliability - or lack thereof.
I recently built a simple block indexer using Viem, a popular TypeScript library for Ethereum. The code looked straightforward enough:
import { createPublicClient, webSocket } from "viem"
import { mainnet } from "viem/chains"
const client = createPublicClient({
chain: mainnet,
transport: webSocket("wss://ethereum-rpc.publicnode.com"),
})
const unwatch = client.watchBlocks({
emitMissed: true,
emitOnBegin: true,
onBlock: (block) => {
const blockNumber = block.number
const blockHashSimple = block.hash.slice(-10)
const blockParentHashSimple = block.parentHash.slice(-10)
console.log(
`n=${blockNumber} h=${blockHashSimple} p=${blockParentHashSimple}`
)
},
onError: (error) => console.log(error),
})
Simple and clean, right? Well, the output tells a different story.
When I ran this code, I started seeing some bizarre behavior. Take a look at this sequence:
n=21486372 h=793290bbf4 p=76d536993d
n=21486373 h=fbb51ac635 p=793290bbf4
n=21486373 h=fbb51ac635 p=793290bbf4
n=21486374 h=5e78d32503 p=fbb51ac635
n=21486375 h=8cfd886a8d p=5e78d32503
n=21486377 h=502ff4bc4b p=3ae8ff4e38
n=21486377 h=502ff4bc4b p=3ae8ff4e38
n=21486378 h=b6a7eaea95 p=502ff4bc4b
n=21486379 h=5344bdb194 p=b6a7eaea95
n=21486380 h=267249a681 p=5344bdb194
n=21486381 h=60927f321f p=267249a681
n=21486383 h=9fdac4c4ec p=9e3d484030
n=21486384 h=6d0a843bb7 p=9fdac4c4ec
Notice that block 21486373 appears twice? That's not a typo - it's actually happening.
You might think, "No problem, I'll just deduplicate the blocks." But wait, there's more. Looking at the full sequence, I noticed that some blocks were completely missing. Blocks 21486376 and 21486382? Gone. Vanished. Never showed up.
My first thought was to use Viem's emitOnMissed: true
parameter. Surely that
would catch the missing blocks, right?
n=21486397 h=85cf600b05 p=c8b7dceda0
n=21486397 h=85cf600b05 p=c8b7dceda0
n=21486399 h=54ee26bc1e p=d28c102fc5
n=21486400 h=5e6ee23e87 p=54ee26bc1e
n=21486401 h=e23caad2e0 p=5e6ee23e87
n=21486402 h=784b681ab5 p=e23caad2e0
Nope. Block 21486398 still slipped through the cracks.
If you're thinking "who cares about a few missing blocks?" - here's why you should:
- Missing blocks mean missing transactions and events
- Duplicate blocks can cause double-processing of data
- Your data integrity is at risk
Instead of relying solely on websockets, here's what you should do:
- Implement a block number tracking system to detect gaps
- Use polling as a fallback mechanism
- Build retry logic for missed blocks
- Add deduplication for repeated blocks
Think of it like building a safety net under your websocket subscription.
Here's what a more robust system might look like:
- Primary data source: Websocket subscription for real-time updates
- Secondary data source: Regular polling to verify block sequence
- Block tracker: Keep track of the last N blocks to detect gaps
- Recovery mechanism: Fetch missing blocks via RPC calls
Conclusion
Websocket subscriptions are great for real-time data, but they shouldn't be your only source of truth. Build your system with the assumption that blocks will be missed or duplicated, and plan accordingly. Your future self (and your users) will thank you.
© 2025