{"record":{"id":"120097c59568167c","repo":"linera-io/linera-protocol","slug":"invalidcommitteeepoch","errorCode":"InvalidCommitteeEpoch","errorMessage":"ExecutionError::InvalidCommitteeEpoch { provided, expected }","messagePattern":"ExecutionError::InvalidCommitteeEpoch (.+?)","errorType":"exception","errorClass":"ExecutionError","httpStatus":null,"severity":"critical","filePath":"linera-execution/src/system.rs","lineNumber":685,"sourceCode":"                    missing_events.is_empty(),\n                    ExecutionError::EventsNotFound(missing_events)\n                );\n            }\n            Checkpoint => {\n                return Err(ExecutionError::InternalError(\n                    \"SystemOperation::Checkpoint must be dispatched at ExecutionStateView level\",\n                ));\n            }\n        }\n\n        Ok(new_application)\n    }\n\n    /// Returns an error if the `provided` epoch is not exactly one higher than the chain's current\n    /// epoch.\n    fn check_next_epoch(&self, provided: Epoch) -> Result<(), ExecutionError> {\n        let expected = self.epoch.get().try_add_one()?;\n        ensure!(\n            provided == expected,\n            ExecutionError::InvalidCommitteeEpoch { provided, expected }\n        );\n        Ok(())\n    }\n\n    async fn credit(&mut self, owner: &AccountOwner, amount: Amount) -> Result<(), ExecutionError> {\n        if owner == &AccountOwner::CHAIN {\n            let new_balance = self.balance.get().saturating_add(amount);\n            self.balance.set(new_balance);\n        } else {\n            let balance = self.balances.get_mut_or_default(owner).await?;\n            *balance = balance.saturating_add(amount);\n        }\n        Ok(())\n    }\n\n    async fn credit_or_send_message(","sourceCodeStart":667,"sourceCodeEnd":703,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-execution/src/system.rs#L667-L703","documentation":"Linera's system runtime throws this when a committee-related operation supplies an epoch that is not exactly one greater than the chain's current epoch. check_next_epoch computes expected = current_epoch + 1 and rejects every other value, so committee epochs must advance sequentially with no gaps, repeats, or rollbacks. The error's fields tell you exactly which epoch was submitted (provided) and which one was required (expected).","triggerScenarios":"Executing AdminOperation::CreateCommittee { epoch, blob_hash } on the admin chain with epoch != current_epoch + 1 (e.g. creating epoch 5 while the chain is at 3, or re-creating an epoch that already exists), or executing SystemOperation::ProcessNewEpoch(epoch) on a user chain whose stored epoch is behind or ahead of the epoch carried by the message.","commonSituations":"Restarting or replaying a local/dev Linera network with leftover storage where the epoch counter was already incremented; a client or wallet that cached a stale epoch; two racing committee-creation proposals where the second submission is now stale; client and validator built from protocol versions whose epoch state has diverged after a network reset or restore.","solutions":["Resubmit with the epoch reported as expected in the error payload: it is the chain's current epoch plus one.","Before building the CreateCommittee operation, query the admin chain's fresh epoch state (e.g. GraphQL block/header epoch or the client chain-info API) instead of trusting a cached value.","If the error came from a retry or replay, check whether that epoch was already created; if so, continue with the existing committee rather than re-submitting.","For dev and test environments with desynchronized state, reset it: linera net reset, wipe local storage directories, or docker compose down -v."],"exampleFix":"// before: hardcoded or stale epoch in the admin operation\nlet op = AdminOperation::CreateCommittee { epoch: Epoch(5), blob_hash };\n\n// after: derive the next epoch from the admin chain's current state\nlet expected = current_epoch.try_add_one()?; // current_epoch + 1\nlet op = AdminOperation::CreateCommittee { epoch: expected, blob_hash };","handlingStrategy":"validation","validationCode":"// Fetch the admin chain's current epoch and derive the next one before submitting\nlet current: Epoch = client.chain_info(admin_chain_id).await?.execution.epoch;\nlet next = current.try_add_one()?;\nensure!(\n    proposed_epoch == next,\n    \"submit epoch {next} (current + 1), got {proposed_epoch}\"\n);","typeGuard":"fn is_invalid_committee_epoch(e: &ExecutionError) -> bool {\n    matches!(e, ExecutionError::InvalidCommitteeEpoch { .. })\n}","tryCatchPattern":"match result {\n    Err(ExecutionError::InvalidCommitteeEpoch { expected, .. }) => {\n        // Epoch state moved on: adopt `expected` as the next epoch,\n        // re-read the admin chain state, and resubmit once.\n    }\n    Err(e) => return Err(e.into()),\n    Ok(value) => { /* ... */ }\n}","preventionTips":["Always derive the new epoch from a fresh read of the admin chain instead of caching or hardcoding it.","Serialize committee-creation proposals so only one submission per epoch is in flight.","After any network reset or restore, re-sync wallets and clients before sending admin operations.","Treat expected in the error as authoritative: it equals current_epoch + 1."],"tags":["linera","epoch","committee","admin-chain","consensus"],"backgroundTag":"epoch-mismatch","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}