datahaven-xyz/datahaven · error · Error
SyncCommitteeUpdateRequired
SyncCommitteeUpdateRequired
Error message
SyncCommitteeUpdateRequired
What it means
Thrown in `verify_update` when the update contains no `next_sync_committee_update` and the update's finalized period is not equal to the store period. The light-client protocol requires that an update within the same period include a next-sync-committee payload unless it is finalized to the current store period; otherwise the client cannot advance safely.
Solutions
- Submit an update that includes the `next_sync_committee_update` for the rotation period before submitting period-skipping finality updates.
- Re-run the bootstrap flow if the client is far behind the sync-committee rotation schedule.
- Adjust the relayer to never skip period boundaries when forwarding updates.
- Verify `store_period` (period of the latest finalized beacon state) and pick updates finalized within it.
Example fix
// before: finality-only update that jumps a period submit(finality_update); // after: include the sync committee rotation let mut update = beacon.get_update(finalized_slot); update.next_sync_committee_update = Some(beacon.get_next_sync_committee()); submit(update);
Defensive patterns
Strategy: validation
Validate before calling
const storePeriod = computePeriod(await getStoreSlot());
const finalizedPeriod = computePeriod(update.finalized_header.slot);
if (!update.next_sync_committee_update && finalizedPeriod !== storePeriod) {
throw new Error('update must include sync committee rotation or stay in store period');
} Type guard
function isFinalityOnlyInPeriod(update, storePeriod) {
return update.next_sync_committee_update == null
&& computePeriod(update.finalized_header.slot) === storePeriod;
} Try / catch
try {
await submit(update);
} catch (e) {
if (String(e).includes('SyncCommitteeUpdateRequired')) {
await submitRotationUpdateThenRetry(update);
}
} Prevention
- Never let the relayer skip a sync-committee period boundary
- Monitor store period vs latest beacon period and alert on gaps
- Re-bootstrap when the client lags more than one period
When it happens
Trigger: Submitting a finality-only update (no sync committee data) whose `finalized_header.slot` falls into a period ahead of `store_period`, i.e. the client skipped past a sync-committee rotation without receiving the new committee.
Common situations: Relayer downtime spanning a sync-committee period boundary, replaying an old update after the stored committee rotated, or a light client that missed the initial committee handover update.
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
- HeaderNotFinalized
- InvalidSyncCommitteeUpdate
- SyncCommitteeParticipantsNotSupermajority
- InvalidExecutionHeaderProof
- InvalidAncestryMerkleProof
AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13).
Data as JSON: /api/errors/3ed88e88ab05bf36.
Report an issue: GitHub.
Appendix: source
Thrown at operator/pallets/ethereum-client/src/lib.rs:440
Error::<T>::InvalidSyncCommitteeUpdate
);
}
let next_sync_committee_gindex = Self::next_sync_committee_gindex_at_slot(
update.attested_header.slot,
fork_versions,
);
ensure!(
verify_merkle_branch(
sync_committee_root,
&next_sync_committee_update.next_sync_committee_branch,
subtree_index(next_sync_committee_gindex),
generalized_index_length(next_sync_committee_gindex),
update.attested_header.state_root
),
Error::<T>::InvalidSyncCommitteeMerkleProof
);
} else {
ensure!(
update_finalized_period == store_period,
Error::<T>::SyncCommitteeUpdateRequired
);
}
// Verify sync committee aggregate signature.
let sync_committee = if signature_period == store_period {
<CurrentSyncCommittee<T>>::get()
} else {
<NextSyncCommittee<T>>::get()
};
let absent_pubkeys =
Self::find_pubkeys(&participation, (*sync_committee.pubkeys).as_ref(), false);
let signing_root = Self::signing_root(
&update.attested_header,
Self::validators_root(),
update.signature_slot,
)?;View on GitHub (pinned to edcb13dbbc)