{"record":{"id":"e68cac194e4438eb","repo":"datahaven-xyz/datahaven","slug":"skippedsynccommitteeperiod","errorCode":"SkippedSyncCommitteePeriod","errorMessage":"SkippedSyncCommitteePeriod","messagePattern":"SkippedSyncCommitteePeriod","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"operator/pallets/ethereum-client/src/lib.rs","lineNumber":337,"sourceCode":"            // Verify sync committee has sufficient participants.\n            let participation =\n                decompress_sync_committee_bits(update.sync_aggregate.sync_committee_bits);\n            Self::sync_committee_participation_is_supermajority(&participation)?;\n\n            // Verify update does not skip a sync committee period.\n            ensure!(\n                update.signature_slot > update.attested_header.slot\n                    && update.attested_header.slot >= update.finalized_header.slot,\n                Error::<T>::InvalidUpdateSlot\n            );\n            // Retrieve latest finalized state.\n            let latest_finalized_state =\n                FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())\n                    .ok_or(Error::<T>::NotBootstrapped)?;\n            let store_period = compute_period(latest_finalized_state.slot);\n            let signature_period = compute_period(update.signature_slot);\n            if <NextSyncCommittee<T>>::exists() {\n                ensure!(\n                    (store_period..=store_period + 1).contains(&signature_period),\n                    Error::<T>::SkippedSyncCommitteePeriod\n                )\n            } else {\n                ensure!(\n                    signature_period == store_period,\n                    Error::<T>::SkippedSyncCommitteePeriod\n                )\n            }\n\n            // Verify update is relevant.\n            let update_attested_period = compute_period(update.attested_header.slot);\n            let update_finalized_period = compute_period(update.finalized_header.slot);\n            let update_has_next_sync_committee = !<NextSyncCommittee<T>>::exists()\n                && (update.next_sync_committee_update.is_some()\n                    && update_attested_period == store_period);\n            ensure!(\n                update.attested_header.slot > latest_finalized_state.slot","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/datahaven-xyz/datahaven/blob/edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec/operator/pallets/ethereum-client/src/lib.rs#L319-L355","documentation":"When no next sync committee is stored yet, the light client only accepts updates whose signature period equals the store period; a `signature_slot` in any later period would jump past an entire sync committee period, which cannot be verified. `verify_update` raises `SkippedSyncCommitteePeriod` in this branch to prevent the client from skipping an unverified period.","triggerScenarios":"Submitting an update while `NextSyncCommittee<T>` does not exist and `compute_period(update.signature_slot) != compute_period(latest_finalized_state.slot)` — i.e. the client missed updates for at least one full sync committee period (~27 hours on mainnet).","commonSituations":"Light client was bootstrapped long ago and the relayer went down for over a day; the pallet was halted during an incident and updates lapsed; operator submits only the latest update instead of catching up period by period.","solutions":["Re-bootstrap the light client from a current bootstrap checkpoint (`bootstrap` extrinsic / trusted checkpoint) so store_period is current.","Catch up with intermediate updates covering each sync committee period in order before submitting the latest one.","Ensure the relayer is running continuously so no full period is ever missed.","If halted periods occurred, restart from a new trusted checkpoint rather than replaying old updates."],"exampleFix":"// before\n client.submit(latest_update); // periods ahead of store\n// after\n let store_period = period(client.latest_finalized_slot());\n for u in updates_in_periods_from(store_period) { client.submit(u)?; }","handlingStrategy":"validation","validationCode":"const storePeriod = period(latestFinalizedSlot);\nif (!<NextSyncCommittee exists> && period(update.signatureSlot) !== storePeriod) throw new Error('would skip sync committee period');","typeGuard":null,"tryCatchPattern":"try { await submit(update); } catch (e) { if (e.includes('SkippedSyncCommitteePeriod')) { await rebootstrap(); } else throw e; }","preventionTips":["Run the relayer continuously with uptime alerts","Catch up period-by-period after downtime","Re-bootstrap when gaps exceed one period"],"tags":["substrate","beacon-consensus","sync-committee","stale-state"],"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"}