linera-io/linera-protocol · error · ChainClientError
Protocol error within chain client: A quorum voted with an u
Error message
Protocol error within chain client: A quorum voted with an unlocking round that does not match the proposal's justification chain
What it means
Raised in Client::submit_block_proposal on the validated-retry path: the winning quorum signed an unlocking_round that does not equal the top unlocking round of the justification chain carried by our proposal. Per the source comment, the quorum may have formed from a competing proposal that cited a different certificate, so its votes sign a different unlocking round; gluing our chain onto those votes would build a certificate that fails verification downstream. The client rejects it as a retryable error rather than assembling the mismatched certificate.
Source
Thrown at linera-core/src/client/mod.rs:1434
}
}
}
});
let quorum = self
.communicate_chain_action(&committee, submit_action, value)
.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)
}
View on GitHub (pinned to 6c226ddcb3)
Solutions
- Retry: the pending-block loop re-synchronizes chain state from validators and re-proposes on top of whatever was validated
- Ensure a single writer per chain (one client/process proposing) to avoid competing proposals
- Align validator versions across the committee
- If it persists on an idle chain, collect the proposals/certificates involved and report a protocol bug
Defensive patterns
Strategy: retry
Type guard
fn is_unlocking_round_mismatch(err: &chain_client::Error) -> bool {
matches!(
err,
chain_client::Error::ProtocolError(
"A quorum voted with an unlocking round that does not match the proposal's \
justification chain"
)
)
} Try / catch
match client.process_pending_block().await {
Err(e) if is_unlocking_round_mismatch(&e) => {
// Competing proposal won the validated quorum; absorb it and rebuild on top.
client.synchronize_chain_state_from_all_validators().await?;
client.process_pending_block().await
}
other => other,
} Prevention
- Serialize proposals: never run two clients proposing the same chain concurrently
- After leader timeouts, always re-synchronize before re-proposing a validated retry
- Treat occasional occurrences as benign contention; only persistent failures on an idle chain warrant a bug report
When it happens
Trigger: Regular (validated) retry of a block proposal while validators concurrently validated a competing proposal citing a different certificate; two proposers racing in the same round; a validator set that latched onto a competing validated block. Confirmed/timeout quorums have unlocking_round None on both sides, so only the validated-retry case triggers this.
Common situations: Concurrent clients retrying a failed block on the same owner chain; leader-timeout churn where multiple proposals circulate; mixed-version validators disagreeing on justification handling during a protocol upgrade.
Related errors
- The new proposal's round must be greater than the original's
- Protocol error within chain client: A quorum voted with a ju
- Must contain a validation certificate if and only if it cont
- Protocol error within chain client: A quorum confirmed with
- Unexpected quorum: validators voted for block hash {hash} in
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/56a9ee5d128fe5a4.
Report an issue: GitHub.