linera-io/linera-protocol · error · ChainError

Cannot vote for block proposal of chain {chain_id} because {

Error message

Cannot vote for block proposal of chain {chain_id} because {} cross-chain message bundle(s) have not been received yet

What it means

On the proposal path (must_be_present=true), remove_bundles_from_inboxes collects every (origin, height) pair whose bundle is not yet in the chain's inbox and fails with MissingCrossChainUpdates listing them all (linera-chain/src/chain.rs:743-777, comment: 'so the caller can be told the full set of cross-chain updates to fetch in a single round-trip'). A block cannot receive messages the local chain state has not yet been delivered via cross-chain updates.

Source

Thrown at linera-chain/src/chain.rs:771

                    .collect::<Vec<_>>()
                    .join(", ")
            );
            for bundle in bundles {
                // Mark the message as processed in the inbox.
                let was_present = inbox
                    .remove_bundle(bundle)
                    .await
                    .map_err(|error| (chain_id, origin, error))?;
                if must_be_present && !was_present {
                    missing_bundles.push((origin, bundle.height));
                }
            }
            inbox.observe_size_metric();
            if inbox.added_bundles.count() == 0 {
                self.nonempty_inboxes.get_mut().remove(&origin);
            }
        }
        ensure!(
            missing_bundles.is_empty(),
            ChainError::MissingCrossChainUpdates {
                chain_id,
                bundles: missing_bundles,
            }
        );
        Ok(())
    }

    /// Returns the chain IDs of all recipients for which a message is waiting in the outbox.
    pub fn nonempty_outbox_chain_ids(&self) -> Vec<ChainId> {
        self.nonempty_outboxes.get().iter().copied().collect()
    }

    /// Returns the outboxes for the given targets, or an error if any of them are missing.
    pub async fn load_outboxes(
        &self,
        targets: &[ChainId],

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Use the error payload: for each missing (origin, height), fetch the sender's confirmed block and run it through the recipient worker's cross-chain update handling, then retry the proposal
  2. Wait for the inbox to be synced (query chain info for pending inbox state) before proposing the receive block
  3. Retry the identical proposal after delivery completes — the error is transient by design

Example fix

// before: proposing immediately after the send confirms
client.propose_receive_block(bundles).await?; // -> MissingCrossChainUpdates

// after: drive delivery, then retry
match client.propose_receive_block(bundles.clone()).await {
    Err(ChainError::MissingCrossChainUpdates { bundles: missing, .. }) => {
        for (origin, height) in missing {
            client.fetch_and_process_sender_block(origin, height).await?;
        }
        client.propose_receive_block(bundles).await?;
    }
    other => other?,
}
Defensive patterns

Strategy: retry

Validate before calling

// Before proposing a receive block, confirm the bundles are in the inbox
// (the error itself lists exactly what is missing, so a pre-check is optional):
let pending = client.query_inbox_state(chain_id).await?;
let ready = block.incoming_bundles().all(|b| pending.contains(b.origin, b.bundle.height));
if !ready {
    client.synchronize_receive_logs(chain_id).await?; // pull cross-chain updates first
}

Try / catch

match client.propose(block).await {
    Err(ChainError::MissingCrossChainUpdates { bundles: missing, .. }) => {
        // `missing` is the exact [(origin, height)] set to fetch — pull those sender
        // blocks, deliver them, then retry the identical proposal.
        for (origin, height) in &missing {
            client.fetch_and_process_sender_block(*origin, *height).await?;
        }
        client.propose(block).await?
    }
    other => other?,
}

Prevention

When it happens

Trigger: Proposing a receive block immediately after the sender's block confirmed, before the recipient validator processed the sender's cross-chain updates; a node restarted without syncing inboxes; message delivery racing block proposal construction.

Common situations: High-latency cross-chain message delivery in test nets or geographically spread validators; clients building receive-blocks eagerly after sending; inbox state lost or pruned after a storage restore.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/76be6995cbc0a821. Report an issue: GitHub.