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

  1. Retry the whole action: synchronize the chain from validators (the client already pulls lag reports) and re-run process_pending_block
  2. 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
  3. Reduce concurrent writers on the chain to one client
  4. 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

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


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