linera-io/linera-protocol · error · ChainClientError
Unexpected quorum: validators voted for block hash {hash} in
Error message
Unexpected quorum: validators voted for block hash {hash} in {round}, expected block hash {expected_hash} in {expected_round} What it means
Raised in Client::communicate_chain_action after communicate_with_quorum succeeds: votes are grouped by their full signed payload (hash, round, unlocking_round, first_round, justification_commitment), and the quorum that reached voting weight may be for a different block hash or round than the value and round this client submitted. The guard compares (votes_hash, votes_round) with (value.hash(), action.round()) and reports both sides. It signals that consensus moved somewhere else while we were asking — e.g. we got a timeout quorum when we asked for a block, or votes for a different block/round.
Source
Thrown at linera-core/src/client/mod.rs:1593
}
result => result,
}
})
},
self.options.quorum_grace_period,
)
.await;
let ((votes_hash, votes_round, _, _, _), votes) = match result {
Ok(quorum) => quorum,
Err(err) => {
// The round failed; absorb whatever the more advanced validators hold before
// surfacing the outcome, so the caller retries on top of a synchronized state.
self.process_lag_reports(lag_reports.into_inner().unwrap())
.await;
return Err(err.into());
}
};
ensure!(
(votes_hash, votes_round) == (value.hash(), action.round()),
chain_client::Error::UnexpectedQuorum {
hash: votes_hash,
round: votes_round,
expected_hash: value.hash(),
expected_round: action.round(),
}
);
// Certificate is valid because
// * `communicate_with_quorum` ensured a sufficient "weight" of
// (non-error) answers were returned by validators.
// * each answer is a vote signed by the expected validator.
let certificate = LiteCertificate::try_from_votes(votes)
.ok_or_else(|| {
chain_client::Error::InternalError(
"Vote values or rounds don't match; this is a bug",
)
})?View on GitHub (pinned to 6c226ddcb3)
Solutions
- Retry the whole action: synchronize the chain from validators (the client already pulls lag reports) and re-run process_pending_block
- If the returned quorum is a timeout/outdated round, treat it as a signal to advance the round and re-propose rather than as a fatal error
- Reduce concurrent writers on the chain to one client
- Check validator clock synchronization if timeouts and proposals keep crossing
Defensive patterns
Strategy: retry
Type guard
fn unexpected_quorum(err: &chain_client::Error) -> Option<(CryptoHash, Round, CryptoHash, Round)> {
match err {
chain_client::Error::UnexpectedQuorum { hash, round, expected_hash, expected_round } => {
Some((*hash, *round, *expected_hash, *expected_round))
}
_ => None,
}
} Try / catch
match client.process_pending_block().await {
Err(e) if unexpected_quorum(&e).is_some() => {
// Validators moved to a different value/round; state is re-synced via lag reports, retry.
client.process_pending_block().await
}
other => other,
} Prevention
- Avoid submitting proposals and leader timeouts for the same round from different processes
- Synchronize validator clocks (the client warns about skew) so rounds are entered consistently
- Design callers to treat a quorum for a different value as a normal retry signal, not a crash
When it happens
Trigger: request_leader_timeout racing with a block proposal in the same round (validators return the other outcome); finalize_block on a height where validators already confirmed a different block; submit_block_proposal where the aggregated quorum is for a competing value in the same round.
Common situations: High-contention chains with multiple proposers; client retries after crashes while the network advanced; clock skew causing rounds to be entered by different clients at different times.
Related errors
- The new proposal's round must be greater than the original's
- Signatures in a certificate must be from different validator
- WrongRound
- InsufficientRound
- InsufficientRoundStrict
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/ae5ea1af870d7abf.
Report an issue: GitHub.