linera-io/linera-protocol · error · ChainClientError

Protocol error within chain client: A quorum voted with a ju

Error message

Protocol error within chain client: A quorum voted with a justification commitment that does not match the proposal's justification chain

What it means

Companion guard to the unlocking-round check in Client::submit_block_proposal: after a quorum forms, the justification commitment signed by the votes must equal the commitment of the justification chain derived from our own proposal (the validated certificate's full chain for a regular retry, empty for fresh/fast proposals). A mismatch means the winning votes were formed over a different justification chain — e.g. a competing proposal — and assembling a certificate from them would fail verification everywhere.

Source

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

            .await?;

        clock_skew_check_handle.await;

        // The justification chain comes from our own proposal, but the winning quorum may have
        // been formed from a competing proposal that cited a different certificate: its votes then
        // sign a different unlocking round and justification commitment, and gluing our chain onto
        // them would build a certificate that fails verification downstream. Reject that here with
        // a retryable error rather than assembling a mismatched certificate. (For confirmed and
        // timeout quorums both sides are `None`, so this only bites the validated-retry case it is
        // meant to guard.)
        ensure!(
            quorum.unlocking_round() == justification.top_unlocking_round(),
            chain_client::Error::ProtocolError(
                "A quorum voted with an unlocking round that does not match the proposal's \
                 justification chain",
            )
        );
        ensure!(
            quorum.justification_commitment() == justification.commitment(quorum.hash()),
            chain_client::Error::ProtocolError(
                "A quorum voted with a justification commitment that does not match the \
                 proposal's justification chain",
            )
        );
        let certificate = T::make_certificate(quorum, justification);
        self.handle_certificate::<T>(certificate.clone()).await?;
        Ok(certificate)
    }

    /// Creates a [`RemoteNodeUpdater`] for the given validator, backed by our local node.
    fn remote_node_updater(
        &self,
        remote_node: RemoteNode<Env::ValidatorNode>,
    ) -> RemoteNodeUpdater<Env::Storage, Env::ValidatorNode> {
        RemoteNodeUpdater {
            remote_node,

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Retry the proposal cycle: re-prepare the chain and let the client rebuild the block on the now-current locking state
  2. Serialize proposals per chain (one active client per owner)
  3. Verify committee-wide version consistency
  4. Report with attached certificates if it repeats without concurrency
Defensive patterns

Strategy: retry

Type guard

fn is_proposal_justification_mismatch(err: &chain_client::Error) -> bool {
    matches!(
        err,
        chain_client::Error::ProtocolError(
            "A quorum voted with a justification commitment that does not match the \
             proposal's justification chain"
        )
    )
}

Try / catch

match client.process_pending_block().await {
    Err(e) if is_proposal_justification_mismatch(&e) => {
        // Winning votes cited a different certificate; drop pending state and retry.
        client.clear_pending_block();
        client.prepare_chain().await?;
        client.process_pending_block().await
    }
    other => other,
}

Prevention

When it happens

Trigger: Same race as the unlocking-round variant: validators aggregated around a competing proposal that cited a different certificate while we submitted ours; our quorum finished on votes whose justification_commitment field differs from justification.commitment(quorum.hash()).

Common situations: Parallel proposers on one chain; client retries after leader timeouts while another proposer's validated certificate spread; inconsistent validator state after partial crashes.

Related errors


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