linera-io/linera-protocol · error · ChainError

UnexpectedBlockHeight

UnexpectedBlockHeight

Error message

Chain is expecting a next block at height {expected_block_height} but the given block is at height {found_block_height} instead

What it means

process_validated_block requires the validated block's height to equal the chain's next_block_height exactly: validated certificates extend the chain one height at a time and must be applied in order. A certificate for a future height, or one for an already-passed height that is not covered by the skip paths, is rejected.

Source

Thrown at linera-core/src/chain_worker/state.rs:882

    /// Processes a validated block issued for this multi-owner chain.
    #[instrument(skip_all, fields(
        chain_id = %self.chain_id(),
        block_height = %certificate.block().header.height
    ))]
    pub(crate) async fn process_validated_block(
        &mut self,
        certificate: ValidatedBlockCertificate,
    ) -> Result<(ChainInfoResponse, NetworkActions, BlockOutcome), WorkerError> {
        let block = certificate.block();

        let header = &block.header;
        let height = header.height;
        // Check that the chain is active and ready for this validated block.
        // Verify the certificate. Returns a catch-all error to make client code more robust.
        self.initialize_and_save_if_needed().await?;
        let tip_state = self.chain.tip_state.get();
        ensure!(
            header.height == tip_state.next_block_height,
            ChainError::UnexpectedBlockHeight {
                expected_block_height: tip_state.next_block_height,
                found_block_height: header.height,
            }
        );
        let (epoch, committee) = self.chain.current_committee().await?;
        check_block_epoch(epoch, header.chain_id, header.epoch)?;
        certificate.check(&committee)?;
        let already_committed_block = self.chain.tip_state.get().already_validated_block(height)?;
        let should_skip_validated_block = || {
            self.chain
                .manager
                .check_validated_block(&certificate)
                .map(|outcome| outcome == manager::Outcome::Skip)
        };
        if already_committed_block || should_skip_validated_block()? {
            // If we just processed the same pending block, return the chain info unchanged.

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Process validated (and confirmed) block certificates strictly in height order, one per height
  2. Query ChainInfo for next_block_height and submit the certificate that matches it
  3. Drop certificates below next_block_height — their blocks are already applied
Defensive patterns

Strategy: validation

Validate before calling

// Only deliver the certificate matching the chain's next height.
let info = client.chain_info(chain_id).await?;
let expected = info.manager.next_block_height;
match certificate.block().header.height.cmp(&expected) {
    Ordering::Equal => client.submit_validated(certificate).await?,
    Ordering::Less => { /* already applied; drop it */ }
    Ordering::Greater => { /* fetch and submit certificates for the gap first */ }
}

Type guard

fn is_unexpected_block_height(e: &ChainError) -> bool {
    matches!(e, ChainError::UnexpectedBlockHeight { .. })
}

Try / catch

match client.submit_validated(certificate).await {
    Err(e) if matches!(e, ref x if x.is_unexpected_block_height()) => {
        // Re-query next_block_height, deliver the missing heights in order, then retry.
    }
    other => other?,
}

Prevention

When it happens

Trigger: process_validated_block (from handle_validated_request) with a certificate whose block height differs from tip_state.next_block_height — skipping heights, delivering out of order, or replaying old certificates after the tip advanced.

Common situations: Out-of-order certificate delivery during chain synchronization; client resuming from stale local state; duplicate submission after the block was already confirmed via another path.

Related errors


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