{"record":{"id":"84e61f79ecece195","repo":"linera-io/linera-protocol","slug":"invalidcrosschainrequest","errorCode":"InvalidCrossChainRequest","errorMessage":"Invalid cross-chain request","messagePattern":"Invalid cross-chain request","errorType":"validation","errorClass":"WorkerError","httpStatus":null,"severity":"error","filePath":"linera-core/src/chain_worker/state.rs","lineNumber":297,"sourceCode":"    /// ones whose epoch has been revoked on the admin chain.\n    ///\n    /// A revoked-epoch bundle is still accepted if (a) it has already been executed by\n    /// anticipation (`bundle.height <= last_anticipated_block_height`), or (b) a later\n    /// bundle in the same batch is in a still-trusted epoch — that bundle's certificate\n    /// transitively re-certifies all preceding ones via prev-hash chaining.\n    pub(crate) async fn select_message_bundles(\n        &self,\n        origin: &ChainId,\n        next_height_to_receive: BlockHeight,\n        last_anticipated_block_height: Option<BlockHeight>,\n        mut bundles: Vec<(Epoch, MessageBundle)>,\n    ) -> Result<Vec<MessageBundle>, WorkerError> {\n        let recipient = self.chain_id();\n        let mut latest_height = None;\n        let mut skipped_len = 0;\n        let mut trusted_len = 0;\n        for (i, (epoch, bundle)) in bundles.iter().enumerate() {\n            ensure!(\n                latest_height <= Some(bundle.height),\n                WorkerError::InvalidCrossChainRequest\n            );\n            latest_height = Some(bundle.height);\n            if bundle.height < next_height_to_receive {\n                skipped_len = i + 1;\n            }\n            let is_revoked = self\n                .storage\n                .is_epoch_revoked(*epoch)\n                .await\n                .map_err(|error| {\n                    WorkerError::ChainError(Box::new(ChainError::ExecutionError(\n                        Box::new(error),\n                        ChainExecutionContext::Block,\n                    )))\n                })?;\n            if !is_revoked || Some(bundle.height) <= last_anticipated_block_height {","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-core/src/chain_worker/state.rs#L279-L315","documentation":"select_message_bundles validates incoming cross-chain update requests: message bundles must appear with non-decreasing heights, because they are appended to the sender chain's outbox in order. A batch whose heights go backwards is malformed and the whole request is rejected before any message is applied.","triggerScenarios":"process_cross_chain_update (from handle_cross_chain_update) with a bundle list where a later bundle's height is lower than an earlier one's.","commonSituations":"Client-side bug when assembling or merging batches of cross-chain messages; corrupted or hand-built cross-chain request; partial retries concatenated out of order; version changes in the request format.","solutions":["Order the bundles by height (preserving the sender's outbox order) before submitting the cross-chain request","Rebuild the batch from the sender chain's outbox state instead of merging partial batches"],"exampleFix":"// before: merged batches may be out of order\nlet bundles = old_retry_bundles.into_iter().chain(new_bundles).collect();\nworker.handle_cross_chain_update(origin, recipient, bundles).await?; // InvalidCrossChainRequest\n\n// after: keep heights non-decreasing before submitting\nlet mut bundles = old_retry_bundles;\nbundles.extend(new_blobs);\nbundles.sort_by(|a, b| a.1.height.cmp(&b.1.height));\nworker.handle_cross_chain_update(origin, recipient, bundles).await?;","handlingStrategy":"validation","validationCode":"// Guarantee non-decreasing heights before submitting a cross-chain update.\nfn bundles_are_ordered(bundles: &[(Epoch, MessageBundle)]) -> bool {\n    bundles.windows(2).all(|w| w[0].1.height <= w[1].1.height)\n}\nif !bundles_are_ordered(&bundles) {\n    bundles.sort_by(|a, b| a.1.height.cmp(&b.1.height));\n}\nworker.handle_cross_chain_update(origin, recipient, bundles).await?;","typeGuard":"fn is_invalid_cross_chain_request(e: &WorkerError) -> bool {\n    matches!(e, WorkerError::InvalidCrossChainRequest)\n}","tryCatchPattern":null,"preventionTips":["Build cross-chain batches from the sender's outbox order instead of merging partial retries","Validate bundle ordering client-side before every submit","When retrying, re-query the outbox rather than concatenating cached batches"],"tags":["cross-chain-messages","validation","ordering","linera"],"backgroundTag":"protocol-validation-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}