linera-io/linera-protocol · warning · ChainError
InsufficientRound
InsufficientRound
Error message
Round number should be at least {0:?} What it means
In check_proposed_block, once past the fast round, a MultiLeader or SingleLeader(0) proposal from a round older than the manager's current round is obsolete. The manager only considers proposals at or above its current round, which advances via timeout certificates and newer proposals.
Source
Thrown at linera-chain/src/manager.rs:313
// 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.
if let Some(vote) = self.validated_vote() {
ensure!(
new_round > vote.round,
ChainError::InsufficientRoundStrict(vote.round)
);View on GitHub (pinned to 6c226ddcb3)
Solutions
- Fetch fresh chain info and build a new proposal at (or above) the current round
- Discard the stale proposal instead of retrying it unchanged
Defensive patterns
Strategy: validation
Validate before calling
let info = client.chain_info(chain_id).await?;
if proposal.content.round < info.manager.current_round {
// Stale proposal: rebuild it at the current round instead of submitting.
return rebuild_proposal(&client, chain_id).await;
} Type guard
fn is_insufficient_round(e: &ChainError) -> bool {
matches!(e, ChainError::InsufficientRound(_))
} Prevention
- Do not cache or retry proposals across rounds
- Refresh chain info after any timeout event before re-proposing
When it happens
Trigger: Re-delivering or retrying a block proposal whose content.round is lower than manager.current_round() after the round moved forward (timeout certificate processed or a newer proposal seen).
Common situations: Retrying a failed proposal submission after the round advanced; client caching proposals across rounds; leader reusing a proposal computed before a timeout.
Related errors
- The new proposal's round must be greater than the original's
- WrongRound
- InsufficientRoundStrict
- MustBeNewerThanLockingBlock
- InsufficientRound
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/e24b8e45c4d4ed7e.
Report an issue: GitHub.