Indexers, RPC providers and missing events

How indexers and RPC providers can miss events, and how to design for reorgs, pagination, log deduplication, block checkpoints, at-least-once processing,…

By Crypto Loop · Updated 2026-10-06T20:53:25.425Z

Why missing events happen

Blockchain data looks linear when it is read block by block, but the systems that deliver it are not perfectly linear. An indexer may read from an RPC endpoint that is briefly behind, rate-limited, serving a fork that later disappears, or returning partial ranges because of pagination limits. A smart contract event may therefore be observed, then later replaced by a different event at the same height, or not observed at all during a temporary outage. The practical problem is not only chain reorganisation. It is also the interaction between transport, storage, query windows, and the client’s own retry logic.

The key idea is that event processing should be designed as an evidence pipeline, not as a one-time fetch. A robust system expects duplication, omission, and delay, and treats every incoming log as provisional until it is checked against a chain position that is stable enough for the application. This is why the same architecture often needs both replayability and reconciliation. If one component fails, the system must be able to re-read a range, compare what was stored, and repair gaps without inventing data or silently skipping it.

Reorgs and finality

A reorganisation occurs when the chain head changes and one branch of blocks is replaced by another. For indexers, the important consequence is that a transaction receipt or log seen at block N may no longer belong to the canonical history. The safest interpretation is that data near the head is tentative until it reaches whatever confirmation depth the application considers acceptable. That depth is a policy choice, not a universal constant, and it should be framed as a risk tolerance, not as a guarantee of immutability.

Finality reduces, but does not eliminate, uncertainty. Some networks offer probabilistic finality, where the chance of reversal becomes low after more confirmations; others have stronger finality rules that make reorgs beyond a finalised checkpoint much less likely. Even then, the indexer still needs logic for exceptional cases: chain halts, client bugs, delayed finality, or inconsistent RPC answers. A good design separates two states for each event: observed and finalised. Observed means it was seen on at least one source. Finalised means the system has decided the underlying block is stable enough to anchor derived data to it.

A useful decision check is this: if deleting and recomputing the last few blocks would be unacceptable, then the system should not treat head data as settled. Instead, keep a rolling revalidation window. The size of that window depends on the network’s behaviour, the application’s tolerance for temporary inconsistency, and the cost of replay. A payments dashboard may prefer slower but safer confirmation. A live analytics view may accept more volatility, so long as it clearly labels data as provisional.

Pagination and range design

Pagination is where many silent gaps begin. RPC interfaces often limit how many logs, blocks, or receipts can be returned in one request. A request that asks for too much may be truncated, time out, or fail in a way that looks like no results. To avoid missing events, the client should advance through the chain using explicit ranges and verify that each range was fully covered before moving on. The basic rule is simple: do not assume an empty response means there was nothing to fetch unless the query window is known to be complete.

Block and log pagination should be designed with backpressure in mind. A very wide range can create expensive responses and increase the chance of partial failures; a very narrow range can create unnecessary request overhead. The right span depends on the chain activity being indexed and the API characteristics of the source. If a block range is dense with logs, the client may need to shrink windows dynamically when responses approach known limits. If a range is sparse, larger windows can reduce overhead without raising much risk. In all cases, the client should persist the exact high-water mark it has fully processed, not just the last block it happened to query.

A practical check is to make the range state explicit. For each batch, store start block, end block, query parameters, response status, and whether the batch was verified as complete. That makes replay logic simpler: incomplete batches can be retried, and completed batches can be skipped unless a later reconciliation pass detects a mismatch. This also reduces dependence on memory-only cursors, which are brittle during crashes or deploys.

Log deduplication and idempotent writes

Logs can appear more than once for reasons that are not bugs in the chain itself. A retry after timeout may re-read the same block range. Multiple RPC sources may each return the same canonical event. A reorg may cause a previously seen event to reappear in a later canonical branch with a different block hash or disappear entirely. Because duplicates are normal, downstream storage must be idempotent. The same event should not create two rows simply because it was delivered twice.

The most common deduplication key is a stable event identity built from chain identifier, transaction hash, log index, and sometimes block hash or receipt context. The exact key should match the guarantee being modelled. If the goal is canonical event storage, then the block hash matters because a log with the same transaction hash and log index can belong to a different branch after reorg. If the goal is a raw delivery ledger, then keeping multiple sightings may be useful, but they should be marked as separate observations rather than merged blindly.

Idempotent writes are stronger than deduplication alone. A write path that can safely repeat the same upsert, delete, or status update makes retries much less dangerous. For example, a record can move through states such as pending, confirmed, and finalised, with each transition keyed by the same logical event ID. If the job crashes after writing half a batch, rerunning the batch should converge to the same end state. The practical decision check is whether every database mutation can be replayed without creating ambiguity about which version is current.

At-least-once processing in practice

At-least-once processing means an event may be delivered one or more times, but should not be lost if the system can recover. This is usually the right target for blockchain indexing because it is easier to deduplicate duplicates than to prove that nothing was missed. Exactly-once processing is often impractical across RPC calls, queues, storage, and reorg handling, because the problem is not only delivery but also chain ambiguity. If the system claims exactness, it still needs a reconciliation mechanism, which in practice makes it behave like at-least-once plus dedupe.

A reliable pipeline typically has three stages. First, fetch a range of blocks or logs and persist the raw response or enough metadata to reconstruct it. Second, normalise and upsert those logs into a canonical store. Third, reconcile the store against checkpoints or later re-reads. If any stage fails, the batch is retried. The retry may reprocess records already seen, so every stage must tolerate duplicates. This is especially important when jobs are sharded by block range, because overlapping retries or out-of-order completion can otherwise create gaps or double counts.

There is a trade-off. At-least-once means downstream consumers must also be designed for repetition. Metrics, notifications, and materialised views should not assume a unique delivery path. A good question to ask is whether a repeated event would merely overwrite the same state, or whether it would trigger an irreversible side effect. If it is the latter, the side effect should be deferred until the event is finalised or gated behind its own dedupe key and audit trail.

Block checkpoints and reconciliation

Block checkpoints are fixed reference points used to resume work, validate progress, and recover from errors. A checkpoint is more useful than a plain cursor because it can record both position and confidence. For example, a checkpoint might include the highest block fully indexed, the latest finalised block acknowledged by the system, and the hash of the block at that height. That makes it possible to detect whether a later read is following the same chain segment or a different branch.

Checkpoints are also how long-running indexers avoid full rescans. If the process restarts, it can resume from the last safe checkpoint and replay a small overlap rather than beginning at genesis or trusting volatile in-memory state. The overlap is important because a checkpoint taken too close to the head can be invalidated by a reorg. By rescanning the overlap, the indexer can delete or amend records that no longer belong to the canonical branch. The overlap size should be treated as a configurable safety margin, not as a magical guarantee.

A practical check is to store checkpoints atomically with the output of the batch they represent. If the data writes succeed but the checkpoint does not, the batch will be replayed, which is acceptable under at-least-once semantics. If the checkpoint advances but the data writes do not, the system can skip unreconciled records, which is not acceptable. Therefore, checkpoint advancement should happen only after the target state is durably written and verified.

RPC quorum and source disagreement

A single RPC endpoint can be enough for development, but production indexers often need more resilience than one source provides. RPC quorum means comparing responses from multiple endpoints and accepting a result only when enough sources agree according to a defined rule. The rule may be strict majority, weighted trust, or a primary-plus-check design. The point is not to eliminate disagreement entirely, but to make disagreement visible and bounded.

Quorum is especially valuable for chain heads, block hashes, and finality-related queries. If one source is lagging or serving a short-lived fork, another source may already have the canonical branch. If one source times out under load, another may still answer. However, quorum is not free. It adds latency, increases request volume, and requires careful handling of ties, missing responses, and source-specific quirks. For that reason, quorum logic should be used where disagreement is costly, not everywhere indiscriminately.

The decision check here is whether a field is safety-critical or merely informative. For safety-critical positions such as canonical block hash or finalised block number, quorums or cross-checks are often worth the overhead. For less critical metadata, a single trusted source may be sufficient if the indexer is prepared to reconcile later. The design should explicitly name the trust model: one source, several sources with quorum, or one primary source with secondary verification.

Worked example: recovering a missed event

Consider a hypothetical indexer that scans blocks 100 to 120 in batches of five. It stores each batch after fetching logs and marks a checkpoint at the highest fully processed block. During one run, the job fetches blocks 100 to 104 successfully and writes the checkpoint at 104. It then fetches 105 to 109, but the RPC times out after returning a partial log set. The job crashes before the checkpoint is updated. On restart, the indexer begins from 100 plus a safety overlap of two blocks, so it re-reads 103 to 109.

Now suppose block 107 was part of a short reorg and its logs changed. The replay sees the new canonical 107 and reprocesses 105 to 109. Because the storage layer uses idempotent upserts keyed by chain ID, transaction hash, and log index, duplicate canonical logs do not create extra rows. Because the old 107 record is marked by block hash, the reconciliation step can detect that the earlier version no longer belongs to the canonical branch and can mark it superseded or delete it, depending on the storage policy. The final checkpoint is advanced only after the replayed batch is durably committed.

This example shows why the safety overlap matters. Without it, the system would resume at 110 and never notice that 107 had changed. Without idempotent writes, the replay would create duplicates. Without canonical reconciliation, the system could preserve both the old and new versions as though they were equally valid. The event was not lost because the pipeline expected retries and comparison, not a one-shot fetch.

Failure scenarios, limits, and a practical risk caveat

Several failures deserve explicit handling. An RPC endpoint may omit logs temporarily because of overload. A pagination window may be too large and silently truncate practical throughput. A reorg may invalidate the last few blocks after they were indexed. Two RPC sources may disagree about the current head because one is delayed. A checkpoint may be written out of order relative to the data batch. None of these failures is rare enough to ignore in a serious indexing design; the question is whether each one is detectable and recoverable.

There are also limits. No checkpoint strategy can guarantee that an unfinalised block will never change. No quorum strategy can help if all sources share the same upstream fault or if the disagreement is caused by a protocol-level ambiguity. No dedupe key can correct an event that was never fetched. And no at-least-once pipeline can prevent duplicates from reaching downstream consumers; it can only make them safe to absorb. The system therefore needs operational visibility: counts of gaps found, replays performed, quorum mismatches, and the lag between observed and finalised data.

Jurisdiction and risk caveat: blockchain data handling can intersect with data retention, consumer protection, audit, tax, recordkeeping, and other local rules, depending on where the system operates and who relies on the output. This article is technical and not legal, compliance, or investment advice. Teams should review their own obligations, tolerance for provisional data, and incident-handling policies before using any indexing design in production.

References