linera-io/linera-protocol · warning · ChainError
WrongRound
WrongRound
Error message
Round number should be {0:?} What it means
In check_proposed_block, a MultiLeader(_) or SingleLeader(0) proposal is rejected while the chain's current round is still Round::Fast and the proposer is not a super owner. This protects the fast round from being preempted: ordinary owners must wait for the fast round to time out before opening a later round; only super owners may bypass it.
Source
Thrown at linera-chain/src/manager.rs:308
if let Some(old_proposal) = self.proposed.get() {
if old_proposal.content == proposal.content {
return Ok(Outcome::Skip); // We have already seen this proposal; nothing to do.
}
}
// When a block is certified, incrementing its height must succeed.
ensure!(
new_block.height < BlockHeight::MAX,
ChainError::BlockHeightOverflow
);
let current_round = self.current_round();
match new_round {
// The proposal from the fast round may still be relevant as a locking block, so
// we don't compare against the current round here.
Round::Fast => {}
Round::MultiLeader(_) | Round::SingleLeader(0) => {
// If the fast round has not timed out yet, only a super owner is allowed to open
// a later round by making a proposal.
ensure!(
self.is_super(&proposal.owner()) || !current_round.is_fast(),
ChainError::WrongRound(current_round)
);
// After the fast round, proposals older than the current round are obsolete.
ensure!(
new_round >= current_round,
ChainError::InsufficientRound(new_round)
);
}
Round::SingleLeader(_) | Round::Validator(_) => {
// After the first single-leader round, only proposals from the current round are relevant.
ensure!(
new_round == current_round,
ChainError::WrongRound(current_round)
);
}
}
// The round of our validation votes is only allowed to increase.View on GitHub (pinned to 6c226ddcb3)
Solutions
- Wait for the fast round to time out, fetch fresh chain info, and rebuild the proposal for the new current round
- Have a super owner sign and submit the proposal if the fast round genuinely must be preempted
- Re-query the chain (ChainInfoQuery / request_leader_timeout) to learn the current round and ownership before proposing
Example fix
// before: proposal built from stale state, submitted while fast round is open let proposal = BlockProposal::new(Round::MultiLeader(0), block, ...); client.submit_proposal(proposal).await?; // WrongRound(Fast) // after: wait for the fast-round timeout, then propose in the fresh current round let info = client.chain_info(chain_id).await?; let round = wait_for_next_round(&client, info).await?; // uses request_leader_timeout let proposal = BlockProposal::new(round, block, ...); client.submit_proposal(proposal).await?;
Defensive patterns
Strategy: validation
Validate before calling
// Fetch fresh state before proposing in a post-fast round.
let info = client.chain_info(chain_id).await?;
let current = info.manager.current_round;
let is_super = info.manager.ownership.is_super_owner(&owner);
if current.is_fast() && !is_super {
// Fast round still open: wait for its timeout instead of submitting.
return wait_for_round_change(&client, chain_id).await;
}
let round = current.max(Round::MultiLeader(0));
let proposal = BlockProposal::new(round, block, /* ... */); Type guard
fn is_wrong_round(e: &ChainError) -> bool {
matches!(e, ChainError::WrongRound(_))
} Try / catch
match client.submit_proposal(proposal).await {
Err(e) if matches!(e, ref x if x.is_wrong_round()) => {
// Re-query chain info and rebuild the proposal at the returned round.
}
other => other?,
} Prevention
- Derive the proposal round from a fresh ChainInfoResponse, never from cached state
- Track the fast-round timeout before attempting multi-leader proposals
- Route preemption through a super owner only when intentional
When it happens
Trigger: Calling try_handle_block_proposal with a MultiLeader(0) or higher MultiLeader proposal when manager.current_round() is Round::Fast and ownership does not list proposal.owner() as a super owner.
Common situations: Client built its proposal from stale chain info and missed that the chain is still in the fast round; a leader retrying immediately after block confirmation instead of waiting out the fast-round timeout; tests that skip round bookkeeping.
Related errors
- The new proposal's round must be greater than the original's
- InsufficientRound
- InsufficientRoundStrict
- MustBeNewerThanLockingBlock
- InsufficientRound
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/484d95503b8b44ea.
Report an issue: GitHub.