{"record":{"id":"b22233609519a97b","repo":"linera-io/linera-protocol","slug":"cannot-reconcile-bundle-with-previous-bundle","errorCode":null,"errorMessage":"Cannot reconcile {bundle:?} with {previous_bundle:?}","messagePattern":"Cannot reconcile (.+?) with (.+?)","errorType":"validation","errorClass":"InboxError","httpStatus":null,"severity":"critical","filePath":"linera-chain/src/inbox.rs","lineNumber":252,"sourceCode":"            }\n            ensure!(\n                previous_bundle.is_skippable(),\n                InboxError::UnskippableBundle {\n                    bundle: previous_bundle\n                }\n            );\n            self.added_bundles.delete_front();\n            tracing::trace!(\"Skipping previously received bundle {:?}\", previous_bundle);\n        }\n        // Reconcile the bundle with the next added bundle, or mark it as removed.\n        let already_known = match self.added_bundles.front().await? {\n            Some(previous_bundle) => {\n                // Rationale: If the two cursors are equal, then the bundles should match.\n                // Otherwise, at this point we know that `self.next_cursor_to_add >\n                // previous_bundle.cursor() > cursor`. Notably, `bundle` will never be\n                // added in the future. Therefore, we should fail instead of adding\n                // it to `self.removed_bundles`.\n                ensure!(\n                    bundle == &previous_bundle,\n                    InboxError::UnexpectedBundle {\n                        previous_bundle,\n                        bundle: bundle.clone(),\n                    }\n                );\n                self.added_bundles.delete_front();\n                tracing::trace!(\"Consuming bundle {:?}\", bundle);\n                true\n            }\n            None => {\n                tracing::trace!(\"Marking bundle as expected: {:?}\", bundle);\n                self.removed_bundles.push_back(bundle.clone());\n                #[cfg(with_metrics)]\n                metrics::REMOVED_BUNDLES\n                    .with_label_values(&[])\n                    .observe(self.removed_bundles.count() as f64);\n                false","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-chain/src/inbox.rs#L234-L270","documentation":"Raised by InboxStateView::remove_bundle when the bundle being consumed matches the cursor of the next added bundle but the two bundles differ (InboxError::UnexpectedBundle). Cursors identify bundle positions per origin; if the cursor is equal the bundles must be byte-equal, otherwise the receiver's stored bundle and the one in the incoming certificate contradict each other. Since the incoming bundle can never be added at that position, remove_bundle fails rather than corrupting the inbox.","triggerScenarios":"Executing a block that removes a bundle at cursor C while the inbox's added_bundles front is a different bundle also at cursor C. Indicates the certificate's block content disagrees with the receiver's inbox: e.g. two conflicting blocks at the same height from the same sender chain, or a forked sender chain delivering divergent bundles at the same cursor.","commonSituations":"Processing a block from a forked sender chain (same height, different content) after already accepting bundles from the other fork; a malicious sender chain signing contradictory cross-chain updates; a bug that swaps bundle contents between cursors when constructing blocks.","solutions":["Identify which of the two blocks at the conflicting cursor is the one finalized by the sender chain, and process only certificates from that fork.","If a fork was resolved on the sender side, wait for the sender's finality before applying its cross-chain updates to receivers.","Do not hand-construct bundles; always derive them from the sender's certified block so content matches at each cursor.","Report the conflicting certificates as evidence of sender misbehavior if both are finalized."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before executing a block, ensure its bundle matches what the inbox holds at that cursor\nif let Some(front) = inbox.added_bundles.front().await? {\n    if front.cursor() == bundle.cursor() {\n        ensure!(\n            *front == *bundle,\n            \"conflicting bundle at cursor {:?}: sender fork\",\n            bundle.cursor()\n        );\n    }\n}","typeGuard":null,"tryCatchPattern":"match inbox.remove_bundle(&bundle).await {\n    Err(InboxError::UnexpectedBundle { previous_bundle, bundle }) => {\n        // evidence of a forked sender chain: halt processing and report both bundles\n        report_sender_fork(origin, previous_bundle, bundle);\n        Err(anyhow::anyhow!(\"sender chain fork detected at cursor {:?}\", bundle.cursor()))\n    }\n    other => other,\n}","preventionTips":["Only apply cross-chain updates from finalized (certified) sender blocks.","Do not re-execute blocks from a losing fork.","Treat UnexpectedBundle as a security event, never as a transient error."],"tags":["inbox","cross-chain-messages","fork","data-integrity"],"backgroundTag":"conflicting-message-bundle-at-cursor","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}