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
- Retry the proposal cycle: re-prepare the chain and let the client rebuild the block on the now-current locking state
- Serialize proposals per chain (one active client per owner)
- Verify committee-wide version consistency
- 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
- Single-writer discipline per chain is the main preventive measure
- Ensure clients restart with synchronized state (prepare_chain) before resubmitting blocks
- Pin the whole committee to one protocol version during rolling upgrades
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
- Protocol error within chain client: A quorum confirmed with
- Protocol error within chain client: A quorum voted with an u
- Certificate justification commitment does not match its just
- Certificate unlocking round does not match the top of its ju
- Justification chain must lie in rounds strictly below the ce
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/69db7e15d903dc9f.
Report an issue: GitHub.