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
- Check before submitting: fetch the client's latest finalized slot and only send updates with `attested_header.slot` greater than it.
- Refetch a fresh update from the consensus client instead of retrying the same failed/stale one.
- Deduplicate relayer submissions (track last submitted slot/period).
- 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
- Compare attested slot to latest finalized slot before submitting
- Deduplicate submissions across relayers
- Refetch fresh updates instead of retrying stale ones
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
- InvalidSyncCommitteeMerkleProof
- InvalidBlockRootsRootMerkleProof
- InvalidUpdateSlot
- SkippedSyncCommitteePeriod
- InvalidFinalizedHeaderGap
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)