linera-io/linera-protocol · error · ChainClientError

Failed to download certificates and update local node to the

Error message

Failed to download certificates and update local node to the next height {target_next_block_height} of chain {chain_id}

What it means

Raised by Client::download_certificates when, after batching certificate downloads from the configured validators, the local node's next_block_height for the chain is still below target_next_block_height. The download loop breaks as soon as process_certificates stops advancing the height, then the final guard fails. It means the validator set collectively could not (or would not) serve the certificates needed to reach the target height.

Source

Thrown at linera-core/src/client/mod.rs:692

                    self.options.certificate_batch_download_hedge_delay,
                )
                .await?;
            let Some(new_info) = self
                .process_certificates(
                    &validators,
                    certificates,
                    None,
                    ProcessConfirmedBlockMode::Execute,
                )
                .await?
            else {
                break;
            };
            assert!(new_info.next_block_height > next_height);
            next_height = new_info.next_block_height;
            info = new_info;
        }
        ensure!(
            target_next_block_height <= info.next_block_height,
            chain_client::Error::CannotDownloadCertificates {
                chain_id,
                target_next_block_height,
            }
        );
        Ok(info)
    }

    /// Loads and processes certificates from local storage for the given chain, from the
    /// current local height up to `end`. Returns the chain info after processing.
    /// If `until_block_time` is `Some`, stops before processing any certificate whose
    /// block timestamp is >= the given value (exclusive).
    async fn load_local_certificates(
        &self,
        chain_id: ChainId,
        end: BlockHeight,
        until_block_time: Option<Timestamp>,

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Retry later: validators may still be catching up with their peers and will serve the certificates afterwards
  2. Check each validator's actual next_block_height via a chain_info query and lower the target to what is reachable, or wait for lagging validators
  3. Update the wallet's validator/committee configuration so it matches the epoch of the certificate being processed
  4. If a validator pruned history, fetch the missing certificates from another validator or proxy that still stores them

Example fix

// before
let info = client.download_certificates(chain_id, target_height).await?;

// after
let info = match client.download_certificates(chain_id, target_height).await {
    Ok(info) => info,
    Err(chain_client::Error::CannotDownloadCertificates { chain_id, target_next_block_height }) => {
        // Ask validators how far they actually are before retrying.
        let reachable = client
            .stage_and_check_validators_for_height(chain_id, target_next_block_height)
            .await?;
        client.download_certificates(chain_id, reachable.min(target_next_block_height)).await?
    }
    other => other?,
};
Defensive patterns

Strategy: retry

Validate before calling

// Before downloading to a target height, ask validators how far they actually are.
async fn reachable_height(client: &Client, chain_id: ChainId, target: BlockHeight) -> Result<BlockHeight, chain_client::Error> {
    let info = client.local_node().chain_info(chain_id).await?;
    // compare with validators' advertised next_block_height and pick the min
    Ok(info.next_block_height.min(target))
}

Type guard

fn cannot_download_certificates(err: &chain_client::Error) -> bool {
    matches!(err, chain_client::Error::CannotDownloadCertificates { .. })
}

Try / catch

let mut retries = 0;
loop {
    match client.download_certificates(chain_id, target).await {
        Ok(info) => break info,
        Err(chain_client::Error::CannotDownloadCertificates { target_next_block_height, .. }) if retries < 3 => {
            retries += 1;
            tokio::time::sleep(Duration::from_secs(5 * retries)).await; // validators may catch up
        }
        Err(e) => return Err(e.into()),
    }
}

Prevention

When it happens

Trigger: receive_certificate_with_checked_signatures on a certificate whose block is higher than what any known validator can prove; synchronize_to_known_height with a target beyond the validators' state; validators that pruned old certificates or are stuck on an older epoch; wallet pointing at a validator list from a different committee/epoch than the certificate's.

Common situations: Faucet or wallet transfers where the recipient client knows a certificate height the validators no longer serve; misconfigured validator endpoints (stale wallet config after a committee change); a validator proxy that answers but returns no usable certificates; network partition during initial chain sync.

Understand the failure class

Related errors


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