{"record":{"id":"8a4130ce4c2f85a1","repo":"linera-io/linera-protocol","slug":"checkpoint-precondition-failed-checkpoint-must-be","errorCode":null,"errorMessage":"Checkpoint precondition failed: Checkpoint must be the first transaction in its block","messagePattern":"Checkpoint precondition failed: Checkpoint must be the first transaction in its block","errorType":"validation","errorClass":"ChainError","httpStatus":null,"severity":"warning","filePath":"linera-chain/src/chain.rs","lineNumber":1343,"sourceCode":"                    .map(|blob| blob.id())\n                    .collect::<BTreeSet<_>>(),\n            ChainError::InternalError(\"published_blobs mismatch\".to_string())\n        );\n\n        if *self.execution_state.system.closed.get() {\n            ensure!(block.has_only_rejected_messages(), ChainError::ClosedChain);\n        }\n\n        Self::check_app_permissions(\n            self.execution_state\n                .system\n                .application_permissions\n                .get()\n                .await?,\n            &block,\n        )?;\n\n        ensure!(\n            !block\n                .transactions\n                .iter()\n                .skip(1)\n                .any(Transaction::is_checkpoint),\n            ChainError::CheckpointPreconditionFailed(\n                \"Checkpoint must be the first transaction in its block\",\n            )\n        );\n        let (origin_cursors, inbox_cursors, outbox_block_hashes) = if block.starts_with_checkpoint()\n        {\n            self.check_checkpoint_preconditions().await?;\n            let origin_cursors = self.collect_inbox_cursors().await?;\n            let inbox_cursors = self.collect_all_inbox_cursors().await?;\n            let hashes = self.collect_unfinalized_block_hashes().await?;\n            (origin_cursors, inbox_cursors, hashes)\n        } else {\n            (Vec::new(), Vec::new(), Vec::new())","sourceCodeStart":1325,"sourceCodeEnd":1361,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-chain/src/chain.rs#L1325-L1361","documentation":"execute_block enforces the structural checkpoint invariant (linera-chain/src/chain.rs:1343-1352): no transaction after index 0 may be a checkpoint — `Transaction::is_checkpoint()` must be false for all but the first position. Per the block docs (data_types/mod.rs:100-103) a checkpoint must be the first and effectively the only transaction of its block. Violating this rejects the block before execution.","triggerScenarios":"Composing a block where a SystemOperation::Checkpoint follows other transactions (SDK appending the checkpoint at the end of a batch); scripts that build 'do everything including checkpoint' blocks; deserialized proposals that were reordered.","commonSituations":"Checkpoint automation naively batched with pending user operations; client code that pushes the checkpoint op onto a non-empty transaction list; migrations constructing blocks field-by-field.","solutions":["Put the checkpoint transaction first — and per the docs, alone — in its own block","If other transactions must run, confirm them in a separate block before the checkpoint block","Add a pre-submit assertion mirroring the check: no is_checkpoint() among transactions[1..]"],"exampleFix":"// before: checkpoint appended after other work\nlet mut txs = user_transactions;\ntxs.push(Transaction::ExecuteOperation(Operation::system(SystemOperation::Checkpoint)));\n\n// after: checkpoint leads its own block\nlet checkpoint_block = ProposedBlock {\n    transactions: vec![Transaction::ExecuteOperation(Operation::system(SystemOperation::Checkpoint))],\n    ..\n};\nclient.submit_block(user_transactions).await?;   // block 1: work\nclient.submit_block(checkpoint_block).await?;      // block 2: checkpoint","handlingStrategy":"validation","validationCode":"// Mirror the structural rule before submitting:\nif block.transactions.iter().skip(1).any(|t| t.is_checkpoint()) {\n    anyhow::bail!(\"checkpoint must be the first transaction; restructure the block\");\n}\n// best practice: put the checkpoint alone in its own block","typeGuard":"fn checkpoint_well_placed(block: &ProposedBlock) -> bool {\n    // no checkpoint after position 0 (execute_block's rule, chain.rs:1343)\n    !block.transactions.iter().skip(1).any(Transaction::is_checkpoint)\n}","tryCatchPattern":"match result {\n    Err(ChainError::CheckpointPreconditionFailed(msg)) => {\n        // structural fix required: move the checkpoint to position 0 / its own block,\n        // then re-propose\n    }\n    other => other?,\n}","preventionTips":["Always place a Checkpoint operation first — ideally alone — in its block","Never append checkpoint ops to a block that already has transactions","Automate checkpoints through a dedicated builder that cannot mix them with user transactions"],"tags":["linera","checkpoint","block-structure","validation","rust"],"backgroundTag":"checkpoint-position-invalid","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}