datahaven-xyz/datahaven · error · Error

InvalidUpdateSlot

InvalidUpdateSlot

Error message

InvalidUpdateSlot

What it means

The update's slot ordering is invalid: the signature slot must be strictly after the attested header slot, and the attested header slot must be at or after the finalized header slot. `verify_update` enforces this temporal invariant before using the update, rejecting updates that would move the light client backwards or skip sync committee periods.

Solutions

  1. Fetch a fresh, well-formed update from a consensus client (`/eth/v1/beacon/light_client/updates`) rather than assembling it manually.
  2. Ensure attested_header slot >= finalized_header slot and signature_slot > attested_header slot before submitting.
  3. Discard updates older than the client's latest finalized state — they are also rejected as irrelevant.
  4. Check the relayer for clock/ordering bugs when batching updates.

Example fix

// before
 submit_update(attested, finalized, sig_slot); // sig_slot computed from latest block
// after
 assert!(sig_slot > attested.slot && attested.slot >= finalized.slot);
 submit_update(attested, finalized, sig_slot);
Defensive patterns

Strategy: validation

Validate before calling

if (!(update.signatureSlot > update.attestedHeader.slot && update.attestedHeader.slot >= update.finalizedHeader.slot)) throw new Error('InvalidUpdateSlot');

Try / catch

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

Prevention

When it happens

Trigger: Submitting an `Update` where `update.signature_slot <= update.attested_header.slot` or `update.attested_header.slot < update.finalized_header.slot` — e.g. a hand-crafted update or one assembled from mixed snapshots.

Common situations: Relayer built an update from a beacon node that returned an older attested header than the finalized checkpoint; custom aggregation code used the wrong slot for the sync aggregate; replaying an out-of-order update after a fresher one.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

        }

        pub(crate) fn process_update(update: &Update) -> DispatchResultWithPostInfo {
            Self::verify_update(update)?;
            Self::apply_update(update)
        }

        /// References and strictly follows <https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#validate_light_client_update>
        /// Verifies that provided next sync committee is valid through a series of checks
        /// (including checking that a sync committee period isn't skipped and that the header is
        /// signed by the current sync committee.
        fn verify_update(update: &Update) -> DispatchResult {
            // Verify sync committee has sufficient participants.
            let participation =
                decompress_sync_committee_bits(update.sync_aggregate.sync_committee_bits);
            Self::sync_committee_participation_is_supermajority(&participation)?;

            // Verify update does not skip a sync committee period.
            ensure!(
                update.signature_slot > update.attested_header.slot
                    && update.attested_header.slot >= update.finalized_header.slot,
                Error::<T>::InvalidUpdateSlot
            );
            // Retrieve latest finalized state.
            let latest_finalized_state =
                FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())
                    .ok_or(Error::<T>::NotBootstrapped)?;
            let store_period = compute_period(latest_finalized_state.slot);
            let signature_period = compute_period(update.signature_slot);
            if <NextSyncCommittee<T>>::exists() {
                ensure!(
                    (store_period..=store_period + 1).contains(&signature_period),
                    Error::<T>::SkippedSyncCommitteePeriod
                )
            } else {
                ensure!(
                    signature_period == store_period,

View on GitHub (pinned to edcb13dbbc)