linera-io/linera-protocol · error · WorkerError
InvalidCrossChainRequest
InvalidCrossChainRequest
Error message
Invalid cross-chain request
What it means
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.
Source
Thrown at linera-core/src/chain_worker/state.rs:297
/// ones whose epoch has been revoked on the admin chain.
///
/// A revoked-epoch bundle is still accepted if (a) it has already been executed by
/// anticipation (`bundle.height <= last_anticipated_block_height`), or (b) a later
/// bundle in the same batch is in a still-trusted epoch — that bundle's certificate
/// transitively re-certifies all preceding ones via prev-hash chaining.
pub(crate) async fn select_message_bundles(
&self,
origin: &ChainId,
next_height_to_receive: BlockHeight,
last_anticipated_block_height: Option<BlockHeight>,
mut bundles: Vec<(Epoch, MessageBundle)>,
) -> Result<Vec<MessageBundle>, WorkerError> {
let recipient = self.chain_id();
let mut latest_height = None;
let mut skipped_len = 0;
let mut trusted_len = 0;
for (i, (epoch, bundle)) in bundles.iter().enumerate() {
ensure!(
latest_height <= Some(bundle.height),
WorkerError::InvalidCrossChainRequest
);
latest_height = Some(bundle.height);
if bundle.height < next_height_to_receive {
skipped_len = i + 1;
}
let is_revoked = self
.storage
.is_epoch_revoked(*epoch)
.await
.map_err(|error| {
WorkerError::ChainError(Box::new(ChainError::ExecutionError(
Box::new(error),
ChainExecutionContext::Block,
)))
})?;
if !is_revoked || Some(bundle.height) <= last_anticipated_block_height {View on GitHub (pinned to 6c226ddcb3)
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
Example fix
// before: merged batches may be out of order let bundles = old_retry_bundles.into_iter().chain(new_bundles).collect(); worker.handle_cross_chain_update(origin, recipient, bundles).await?; // InvalidCrossChainRequest // after: keep heights non-decreasing before submitting let mut bundles = old_retry_bundles; bundles.extend(new_blobs); bundles.sort_by(|a, b| a.1.height.cmp(&b.1.height)); worker.handle_cross_chain_update(origin, recipient, bundles).await?;
Defensive patterns
Strategy: validation
Validate before calling
// Guarantee non-decreasing heights before submitting a cross-chain update.
fn bundles_are_ordered(bundles: &[(Epoch, MessageBundle)]) -> bool {
bundles.windows(2).all(|w| w[0].1.height <= w[1].1.height)
}
if !bundles_are_ordered(&bundles) {
bundles.sort_by(|a, b| a.1.height.cmp(&b.1.height));
}
worker.handle_cross_chain_update(origin, recipient, bundles).await?; Type guard
fn is_invalid_cross_chain_request(e: &WorkerError) -> bool {
matches!(e, WorkerError::InvalidCrossChainRequest)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- MetaMask is not connected with the requested owner: ${owner}
- Incoming message bundle in block proposed to {chain_id} has
- Checkpoint precondition failed: Checkpoint must be the first
- {bundle:?} is out of order. Block and height should be at le
- {bundle:?} cannot be skipped: it must be received before the
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/84e61f79ecece195.
Report an issue: GitHub.