linera-io/linera-protocol · warning · ChainError

InsufficientRound

InsufficientRound

Error message

Round number should be at least {0:?}

What it means

PendingBlobsView::update maintains the blobs a validator is still waiting on for the pending block at a given round. It refuses updates carrying a round lower than the recorded one: blob content submitted for an obsolete round is discarded as stale, while a strictly newer round clears the old pending set before inserting.

Source

Thrown at linera-chain/src/pending_blobs.rs:79

    pub async fn maybe_insert(&mut self, blob: &Blob) -> Result<bool, ViewError> {
        let blob_id = blob.id();
        let Some(maybe_blob) = self.pending_blobs.get_mut(&blob_id).await? else {
            return Ok(false);
        };
        if maybe_blob.is_none() {
            *maybe_blob = Some(blob.clone());
        }
        Ok(true)
    }

    pub fn update(
        &mut self,
        round: Round,
        validated: bool,
        maybe_blobs: BTreeMap<BlobId, Option<Blob>>,
    ) -> Result<(), ChainError> {
        let existing_round = *self.round.get();
        ensure!(
            existing_round <= round,
            ChainError::InsufficientRound(existing_round)
        );
        if existing_round < round {
            self.clear();
            self.round.set(round);
            self.validated.set(validated);
        }
        for (blob_id, maybe_blob) in maybe_blobs {
            self.pending_blobs.insert(&blob_id, maybe_blob)?;
        }
        Ok(())
    }
}

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Re-fetch chain info; if the round moved, re-submit the current block proposal so pending blobs track the new round, then upload the blob content
  2. Drop stale blob-upload retries when the recorded round has moved past your proposal's round
Defensive patterns

Strategy: validation

Validate before calling

// Before uploading pending blob content, confirm the round still matches.
let info = client.chain_info(chain_id).await?;
if info.manager.pending_blobs.round > my_proposal_round {
    // A newer proposal registered its blobs: refresh state before uploading.
    return resubmit_current_proposal(&client, chain_id).await;
}
client.upload_blob(chain_id, blob).await?;

Type guard

fn is_insufficient_round(e: &ChainError) -> bool {
    matches!(e, ChainError::InsufficientRound(_))
}

Prevention

When it happens

Trigger: Uploading pending blob content (after a proposal returned BlobsNotFound and the worker registered pending blobs via load_proposal_blobs) using the round of an older proposal, after a proposal in a higher round re-registered pending blobs.

Common situations: Retrying blob uploads from stale client state after the leader re-proposed in a higher round; concurrent proposals from different owners on permissionless multi-leader chains; duplicate submissions racing round advancement.

Related errors


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