You're watching a token transfer confirm. One second it's there, block 17,842,301, fifty thousand USDC moving from wallet A to wallet B. Then the chain quietly swallows it whole, and two indexers that agreed on everything thirty seconds ago are now telling completely different stories about who owns what.

This is not a bug report. It's an architecture problem baked into how most indexing services are built, and it surfaces every time the chain does something the happy path wasn't designed to handle.

When the Chain Walks Something Back

A blockchain reorganization happens when a competing chain of blocks, one that accumulated more cumulative proof-of-work (or, post-Merge on Ethereum, more attestation weight, meaning validator signatures backing a particular chain tip), replaces the chain a node was previously following. The network converges on the heavier chain. The lighter one gets orphaned.

Simple enough in theory.

The problem for indexers is timing. An indexer is not a passive observer. It's a machine that reads blocks as they arrive, parses events, writes records to a database, and exposes that data through an API. By the time a reorg is confirmed, the indexer may have already committed several orphaned blocks' worth of data as gospel. Now it has to walk that back, and this is where services diverge, sometimes permanently, until a human notices.

The Three Places the Logic Splits

There's no single failure mode. Divergence happens at one of three points, and different services fail at different ones.

Detection latency. An indexer learns about a reorg when its connected node signals that the canonical head has changed. If Service A connects to a well-peered full node that sees the reorganization within two seconds, and Service B connects to a lagging node that takes twenty seconds to reconcile, Service B has spent twenty seconds continuing to index what is now a dead chain. Any application that queried Service B in that window got stale data. Most of the time this resolves itself. Sometimes a write-through cache (a layer that immediately persists query results to speed up future reads) means the stale data persists well past the reconciliation point.

Rollback depth. Once an indexer detects a reorg, it needs to decide how far back to unwind. A shallow reorg of one or two blocks is common, almost routine. A deep reorg of six or seven blocks is rare but not unheard of on proof-of-work chains, especially on testnets. The critical variable is how the indexer stores its rollback state. Some services store a full event log with block hashes attached, which makes unwinding clean. Others write to a normalized relational database optimized for query speed, which means rollback requires either soft-deletion flags or a separate revert table. If that revert table wasn't populated correctly during the orphaned blocks, the unwind is incomplete. You end up with phantom token transfers that never happened, or missing ones that did.

Confirmation thresholds. Many indexers let developers configure a finality depth: only expose data from blocks at least N confirmations deep. On Ethereum post-Merge, finalized blocks (those that have passed through the two-epoch checkpoint process, roughly twelve to fifteen minutes of elapsed time) are essentially irreversible. An indexer that treats "finalized" as its threshold will never expose data from a reorged block, but it will also lag by twelve to fifteen minutes on every event. An indexer configured at one or two confirmations is faster but carries reorg exposure. The divergence happens when two services in the same stack use different thresholds without the developer realizing it.

A Worked Scenario: Two Indexers, One Messy Block

Picture a small DeFi protocol. Their backend uses two indexing services for redundancy: call them Apex and Beacon. Both are watching the same ERC-20 contract for Transfer events.

At block 14,500,000, a transfer of 50,000 USDC from wallet A to wallet B is included. Apex indexes it immediately at one confirmation. Beacon waits for three. A reorg occurs at block 14,500,001 that orphans block 14,500,000, and the canonical chain replaces that block with a new one that does not include the transfer.

Apex, having already written the 50,000 USDC transfer, now has to roll it back. Its rollback logic checks for a revert handler keyed by block hash. That handler runs correctly. Apex's database is clean.

Beacon never indexed the transfer in the first place. It was still waiting for its third confirmation when the reorg was detected. Beacon's database is also clean, having done nothing.

So far they agree. But Apex's API response for wallet B's balance history now shows a transfer that appeared, then vanished, recorded in its audit log. Beacon's shows nothing. A compliance tool reading both logs flags a discrepancy. The protocol's support team spends two hours chasing a transaction that was never real.

The data is eventually consistent. The audit trail is not.

The Confirmation Threshold Trap

Most developers set confirmation thresholds once, during initial integration, and never revisit them. This is, in my view, the single most underappreciated source of long-term indexer divergence in production stacks.

Consider Ethereum's proof-of-stake finality: probabilistic in the short term, cryptoeconomically hard past the finalized checkpoint. An indexer configured at six confirmations (a carryover from proof-of-work heuristics, where six blocks represented a reasonable safety margin) is applying a rule of thumb that predates the current consensus mechanism. It's not wrong, exactly. It's just miscalibrated, like navigating a modern city with a map drawn before half the roads were built.

The same mistake runs in the other direction on chains with faster block times. A threshold of six confirmations on a chain producing blocks every 400 milliseconds is a very different wait than six confirmations on a chain producing blocks every 12 seconds. Obvious when stated. Rarely caught in practice.

And here's the question worth sitting with: if your two indexers have different confirmation thresholds and you didn't set them that way intentionally, do you actually know which one your application trusts when they disagree?

You can check your own indexer's behavior by querying the same block hash across two services immediately after a known testnet reorg. If the responses diverge for more than one polling cycle, you have a detection latency problem. If they converge on data but differ in their event history, you have a rollback logic problem. The test takes twenty minutes and it will tell you more than any documentation.

The Honest Complexity of "Eventually Consistent"

Indexing services advertise eventual consistency as a feature. It is a feature. It is also a description of a window during which your application's view of the chain is wrong, and the length of that window varies by service, by node connectivity, by rollback implementation, and by how deep the reorg actually went.

The services aren't lying. The chain itself is non-deterministic at the tip. What diverges isn't the truth of the chain; it's the timestamp at which each indexer arrives at that truth, and whether their internal state was clean enough to make the journey without leaving debris behind.

For any application where ordering matters, where event history is the product, the confirmation threshold isn't a performance tuning knob. It's a commitment about how much reorg risk you're willing to hand to your users. Set it deliberately, document it explicitly, and compare it against every other indexer in your stack. The ones that don't match are the ones that will disagree at exactly the wrong moment, and by then you'll be the one explaining the discrepancy.