{"record":{"id":"3b709869b088f7dd","repo":"linera-io/linera-protocol","slug":"checkpoint-precondition-failed-chain-has-consumed","errorCode":null,"errorMessage":"Checkpoint precondition failed: chain has consumed system events","messagePattern":"Checkpoint precondition failed: chain has consumed system events","errorType":"validation","errorClass":"ChainError","httpStatus":null,"severity":"error","filePath":"linera-chain/src/chain.rs","lineNumber":1650,"sourceCode":"    /// no *system* event stream tracker is set.\n    ///\n    /// The structural invariant that `Checkpoint` must be the *first* transaction in its\n    /// block is enforced unconditionally in `execute_block`, independently of these\n    /// preconditions. Sender-side event conditions are validated inside\n    /// `ExecutionStateView::prepare_checkpoint`.\n    async fn check_checkpoint_preconditions(&self) -> Result<(), ChainError> {\n        let mut had_system_event_tracker = false;\n        self.next_expected_events\n            .for_each_index_while(|stream_id| {\n                if matches!(stream_id.application_id, GenericApplicationId::System) {\n                    had_system_event_tracker = true;\n                    Ok(false)\n                } else {\n                    Ok(true)\n                }\n            })\n            .await?;\n        ensure!(\n            !had_system_event_tracker,\n            ChainError::CheckpointPreconditionFailed(\"chain has consumed system events\")\n        );\n\n        Ok(())\n    }\n\n    /// Returns the hashes of all blocks we have at the given heights, in input order.\n    /// Unknown heights are skipped.\n    #[instrument(skip_all, fields(\n        chain_id = %self.chain_id(),\n        next_block_height = %self.tip_state.get().next_block_height,\n    ))]\n    pub async fn block_hashes_for_heights(\n        &self,\n        heights: impl IntoIterator<Item = BlockHeight>,\n    ) -> Result<Vec<CryptoHash>, ChainError> {\n        let heights = heights.into_iter().collect::<Vec<_>>();","sourceCodeStart":1632,"sourceCodeEnd":1668,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-chain/src/chain.rs#L1632-L1668","documentation":"check_checkpoint_preconditions (linera-chain/src/chain.rs:1638-1656) runs when a block starts with a Checkpoint operation: it scans next_expected_events and rejects the checkpoint if any stream belongs to the System application — i.e., the chain has pending, unconsumed system events (an active system event subscription whose next expected events have not arrived and been processed). Checkpointing requires that system event state to be clean first.","triggerScenarios":"Proposing a Checkpoint block right after subscribing to system event streams, before the subscribed events were delivered and consumed; a checkpoint automation racing event-stream updates on the same chain.","commonSituations":"Checkpoint/backup tooling that snapshots chains on a timer while event subscriptions are active; chains used as event relays where system stream traffic is frequent, so the window of 'no pending system events' is narrow.","solutions":["Let the pending system events be delivered and processed (confirm the blocks carrying them) before proposing the checkpoint","Retry the checkpoint proposal after the event stream quiets down — the condition is state-based and clears itself","If the subscription is no longer needed, stop it so no new system events are expected"],"exampleFix":"// before: proposing the checkpoint immediately\nclient.submit_block(vec![checkpoint_tx]).await?; // CheckpointPreconditionFailed\n\n// after: drain system events, then checkpoint (with retry)\nloop {\n    client.process_system_events(chain_id).await?; // deliver + confirm pending events\n    match client.submit_block(vec![checkpoint_tx.clone()]).await {\n        Ok(_) => break,\n        Err(ChainError::CheckpointPreconditionFailed(_)) => continue,\n        Err(e) => return Err(e.into()),\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.submit_block(checkpoint_block).await {\n    Err(ChainError::CheckpointPreconditionFailed(\"chain has consumed system events\")) => {\n        // pending system events exist: deliver and confirm them, then retry the\n        // same checkpoint proposal (the condition clears once streams are drained)\n    }\n    other => other?,\n}","preventionTips":["Run checkpoints when system event streams are quiet, not right after subscribing","Deliver pending system events before proposing checkpoint blocks","Schedule checkpoint retries — the precondition is state-based and resolves itself"],"tags":["linera","checkpoint","events","streams","rust"],"backgroundTag":"checkpoint-precondition-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}