linera-io/linera-protocol · error · ChainError

Checkpoint precondition failed: chain has consumed system ev

Error message

Checkpoint precondition failed: chain has consumed system events

What it means

check_checkpoint_preconditions (linera-chain/src/chain.rs:1638-1656) runs when a block starts with a Checkpoint operation: it scans next_expected_events and rejects the checkpoint if any stream belongs to the System application — i.e., the chain has pending, unconsumed system events (an active system event subscription whose next expected events have not arrived and been processed). Checkpointing requires that system event state to be clean first.

Source

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

    /// no *system* event stream tracker is set.
    ///
    /// The structural invariant that `Checkpoint` must be the *first* transaction in its
    /// block is enforced unconditionally in `execute_block`, independently of these
    /// preconditions. Sender-side event conditions are validated inside
    /// `ExecutionStateView::prepare_checkpoint`.
    async fn check_checkpoint_preconditions(&self) -> Result<(), ChainError> {
        let mut had_system_event_tracker = false;
        self.next_expected_events
            .for_each_index_while(|stream_id| {
                if matches!(stream_id.application_id, GenericApplicationId::System) {
                    had_system_event_tracker = true;
                    Ok(false)
                } else {
                    Ok(true)
                }
            })
            .await?;
        ensure!(
            !had_system_event_tracker,
            ChainError::CheckpointPreconditionFailed("chain has consumed system events")
        );

        Ok(())
    }

    /// Returns the hashes of all blocks we have at the given heights, in input order.
    /// Unknown heights are skipped.
    #[instrument(skip_all, fields(
        chain_id = %self.chain_id(),
        next_block_height = %self.tip_state.get().next_block_height,
    ))]
    pub async fn block_hashes_for_heights(
        &self,
        heights: impl IntoIterator<Item = BlockHeight>,
    ) -> Result<Vec<CryptoHash>, ChainError> {
        let heights = heights.into_iter().collect::<Vec<_>>();

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Let the pending system events be delivered and processed (confirm the blocks carrying them) before proposing the checkpoint
  2. Retry the checkpoint proposal after the event stream quiets down — the condition is state-based and clears itself
  3. If the subscription is no longer needed, stop it so no new system events are expected

Example fix

// before: proposing the checkpoint immediately
client.submit_block(vec![checkpoint_tx]).await?; // CheckpointPreconditionFailed

// after: drain system events, then checkpoint (with retry)
loop {
    client.process_system_events(chain_id).await?; // deliver + confirm pending events
    match client.submit_block(vec![checkpoint_tx.clone()]).await {
        Ok(_) => break,
        Err(ChainError::CheckpointPreconditionFailed(_)) => continue,
        Err(e) => return Err(e.into()),
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

match client.submit_block(checkpoint_block).await {
    Err(ChainError::CheckpointPreconditionFailed("chain has consumed system events")) => {
        // pending system events exist: deliver and confirm them, then retry the
        // same checkpoint proposal (the condition clears once streams are drained)
    }
    other => other?,
}

Prevention

When it happens

Trigger: Proposing a Checkpoint block right after subscribing to system event streams, before the subscribed events were delivered and consumed; a checkpoint automation racing event-stream updates on the same chain.

Common situations: Checkpoint/backup tooling that snapshots chains on a timer while event subscriptions are active; chains used as event relays where system stream traffic is frequent, so the window of 'no pending system events' is narrow.

Related errors


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