datahaven-xyz/datahaven · warning · Error

IrrelevantUpdate

IrrelevantUpdate

Error message

IrrelevantUpdate

What it means

The update does not advance the light client: its attested header slot is not newer than the latest finalized state slot, and it also does not provide a needed next sync committee update. `verify_update` rejects such updates as `IrrelevantUpdate` to avoid wasted work and replay of old data.

Solutions

  1. Check before submitting: fetch the client's latest finalized slot and only send updates with `attested_header.slot` greater than it.
  2. Refetch a fresh update from the consensus client instead of retrying the same failed/stale one.
  3. Deduplicate relayer submissions (track last submitted slot/period).
  4. Only omit `next_sync_committee_update` when the client already stores a next committee; otherwise include it.

Example fix

// before
 submit(update); // retry loop
// after
 if update.attested_header.slot > client.latest_finalized_slot() {
     submit(update)?;
 }
Defensive patterns

Strategy: validation

Validate before calling

const latest = await api.query.ethereumClient.latestFinalizedBlockRoot();
if (update.attestedHeader.slot <= latestSlot && !needsNextSyncCommittee(update)) throw new Error('irrelevant update');

Try / catch

try { await submit(update); } catch (e) { if (e.includes('IrrelevantUpdate')) { skipStaleUpdate(); } else throw e; }

Prevention

When it happens

Trigger: Submitting an update where `update.attested_header.slot <= latest_finalized_state.slot` and either `next_sync_committee_update` is `None` or `compute_period(update.attested_header.slot) != store_period` — i.e. a stale or duplicate update.

Common situations: Relayer replayed an update already processed (duplicate submission); two relayers race and the slower one's update becomes stale; the operator re-submits an old update after a tx failure without refetching.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13). Data as JSON: /api/errors/3f4b4bd8fdffa0db. Report an issue: GitHub.

Appendix: source

Thrown at operator/pallets/ethereum-client/src/lib.rs:354

            if <NextSyncCommittee<T>>::exists() {
                ensure!(
                    (store_period..=store_period + 1).contains(&signature_period),
                    Error::<T>::SkippedSyncCommitteePeriod
                )
            } else {
                ensure!(
                    signature_period == store_period,
                    Error::<T>::SkippedSyncCommitteePeriod
                )
            }

            // Verify update is relevant.
            let update_attested_period = compute_period(update.attested_header.slot);
            let update_finalized_period = compute_period(update.finalized_header.slot);
            let update_has_next_sync_committee = !<NextSyncCommittee<T>>::exists()
                && (update.next_sync_committee_update.is_some()
                    && update_attested_period == store_period);
            ensure!(
                update.attested_header.slot > latest_finalized_state.slot
                    || update_has_next_sync_committee,
                Error::<T>::IrrelevantUpdate
            );

            // Verify the finalized header gap between the current finalized header and new imported
            // header is not larger than the sync committee period, otherwise we cannot do
            // ancestry proofs for execution headers in the gap.
            ensure!(
                latest_finalized_state
                    .slot
                    .saturating_add(config::SLOTS_PER_HISTORICAL_ROOT as u64)
                    >= update.finalized_header.slot,
                Error::<T>::InvalidFinalizedHeaderGap
            );

            let fork_versions = T::ForkVersions::get();
            let finalized_root_gindex = Self::finalized_root_gindex_at_slot(

View on GitHub (pinned to edcb13dbbc)