{"record":{"id":"1b6a589125711504","repo":"linera-io/linera-protocol","slug":"mustbenewerthanlockingblock","errorCode":"MustBeNewerThanLockingBlock","errorMessage":"Proposal for height {0:?} is not newer than locking block in round {1:?}","messagePattern":"Proposal for height (.+?) is not newer than locking block in round (.+?)","errorType":"validation","errorClass":"ChainError","httpStatus":null,"severity":"warning","filePath":"linera-chain/src/manager.rs","lineNumber":335,"sourceCode":"            }\n            Round::SingleLeader(_) | Round::Validator(_) => {\n                // After the first single-leader round, only proposals from the current round are relevant.\n                ensure!(\n                    new_round == current_round,\n                    ChainError::WrongRound(current_round)\n                );\n            }\n        }\n        // The round of our validation votes is only allowed to increase.\n        if let Some(vote) = self.validated_vote() {\n            ensure!(\n                new_round > vote.round,\n                ChainError::InsufficientRoundStrict(vote.round)\n            );\n        }\n        // A proposal that isn't newer than the locking block is not relevant anymore.\n        if let Some(locking_block) = self.locking_block.get() {\n            ensure!(\n                locking_block.round() < new_round,\n                ChainError::MustBeNewerThanLockingBlock(new_block.height, locking_block.round())\n            );\n        }\n        // If we have voted to confirm a block, we may only vote to validate a *different* block\n        // if a validated block certificate justifies it from a round strictly after our\n        // confirmation. The validation vote will then sign the unlocking round `certificate.round`,\n        // and since our confirmation is in an earlier round, the claim \"I have not voted to confirm\n        // a different block in any round at or above the unlocking round\" stays truthful.\n        //\n        // Re-validating the very block we confirmed is also allowed, but the certificate must\n        // still be at least as recent as our confirmation. The unlocking round only constrains\n        // switching blocks, yet the round we sign is a claim about *ourselves*: an earlier\n        // confirmation of a different block could fall at or above an older certificate's round\n        // and turn the claim into a lie we could be slashed for. Our confirmed vote sits in the\n        // highest round we ever confirmed in, so `vote.round <= certificate.round` guarantees no\n        // different-block confirmation lies in the unlocking window `[certificate.round, round)`.\n        if let Some(vote) = self.confirmed_vote() {","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-chain/src/manager.rs#L317-L353","documentation":"The locking block is the last block this manager voted to validate, backed by a quorum certificate. check_proposed_block requires every new proposal to be in a round strictly later than the locking block's round; otherwise the proposal is irrelevant and cannot displace the locked value.","triggerScenarios":"Submitting a proposal whose round is <= locking_block.round() while the manager holds a lock from a validated certificate — typically a conflicting proposal trying to replace the locked block without a higher-round justification.","commonSituations":"Leader retries its old block after a different block got locked in the same or a higher round; client missed the validated certificate that moved the lock; same-height fork racing.","solutions":["Advance to a strictly higher round (via timeout certificates) before re-proposing","If re-proposing the same locked block in a new round, attach the unlocking validated certificate as OriginalProposal::Regular so the vote can be justified"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let info = client.chain_info(chain_id).await?;\nif let Some(lock) = info.manager.locking_block() {\n    if proposal.content.round <= lock.round() {\n        // Must re-propose strictly above the locking round.\n        return advance_round_via_timeouts(&client, chain_id).await;\n    }\n}","typeGuard":"fn is_must_be_newer_than_locking_block(e: &ChainError) -> bool {\n    matches!(e, ChainError::MustBeNewerThanLockingBlock(_, _))\n}","tryCatchPattern":null,"preventionTips":["Track the locking block's round in client state alongside your own proposals","Attach unlocking validated certificates when re-proposing a locked block in a new round"],"tags":["consensus","locking-block","round","linera"],"backgroundTag":"consensus-lock-conflict","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}