{"record":{"id":"3ed88e88ab05bf36","repo":"datahaven-xyz/datahaven","slug":"synccommitteeupdaterequired","errorCode":"SyncCommitteeUpdateRequired","errorMessage":"SyncCommitteeUpdateRequired","messagePattern":"SyncCommitteeUpdateRequired","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"operator/pallets/ethereum-client/src/lib.rs","lineNumber":440,"sourceCode":"                        Error::<T>::InvalidSyncCommitteeUpdate\n                    );\n                }\n                let next_sync_committee_gindex = Self::next_sync_committee_gindex_at_slot(\n                    update.attested_header.slot,\n                    fork_versions,\n                );\n                ensure!(\n                    verify_merkle_branch(\n                        sync_committee_root,\n                        &next_sync_committee_update.next_sync_committee_branch,\n                        subtree_index(next_sync_committee_gindex),\n                        generalized_index_length(next_sync_committee_gindex),\n                        update.attested_header.state_root\n                    ),\n                    Error::<T>::InvalidSyncCommitteeMerkleProof\n                );\n            } else {\n                ensure!(\n                    update_finalized_period == store_period,\n                    Error::<T>::SyncCommitteeUpdateRequired\n                );\n            }\n\n            // Verify sync committee aggregate signature.\n            let sync_committee = if signature_period == store_period {\n                <CurrentSyncCommittee<T>>::get()\n            } else {\n                <NextSyncCommittee<T>>::get()\n            };\n            let absent_pubkeys =\n                Self::find_pubkeys(&participation, (*sync_committee.pubkeys).as_ref(), false);\n            let signing_root = Self::signing_root(\n                &update.attested_header,\n                Self::validators_root(),\n                update.signature_slot,\n            )?;","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/datahaven-xyz/datahaven/blob/edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec/operator/pallets/ethereum-client/src/lib.rs#L422-L458","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: finality-only update that jumps a period\nsubmit(finality_update);\n// after: include the sync committee rotation\nlet mut update = beacon.get_update(finalized_slot);\nupdate.next_sync_committee_update = Some(beacon.get_next_sync_committee());\nsubmit(update);","handlingStrategy":"validation","validationCode":"const storePeriod = computePeriod(await getStoreSlot());\nconst finalizedPeriod = computePeriod(update.finalized_header.slot);\nif (!update.next_sync_committee_update && finalizedPeriod !== storePeriod) {\n  throw new Error('update must include sync committee rotation or stay in store period');\n}","typeGuard":"function isFinalityOnlyInPeriod(update, storePeriod) {\n  return update.next_sync_committee_update == null\n    && computePeriod(update.finalized_header.slot) === storePeriod;\n}","tryCatchPattern":"try {\n  await submit(update);\n} catch (e) {\n  if (String(e).includes('SyncCommitteeUpdateRequired')) {\n    await submitRotationUpdateThenRetry(update);\n  }\n}","preventionTips":["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"],"tags":["blockchain","consensus","light-client"],"backgroundTag":"invalid-state-transition","analyzedSha":"edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec","analyzedAt":"2026-09-13T19:19:32.206Z","contentChangedAt":"2026-09-13T19:19:32.206Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}