linera-io/linera-protocol · error · WorkerError
InvalidEpoch
InvalidEpoch
Error message
Unexpected epoch {epoch}: chain {chain_id} is at {chain_epoch} What it means
process_timeout only accepts a timeout certificate whose epoch equals the chain's current epoch: the certificate's signatures were collected from a specific committee, and validators of a different epoch are no longer authoritative for this chain.
Source
Thrown at linera-core/src/chain_worker/state.rs:797
))]
pub(crate) async fn process_timeout(
&mut self,
certificate: TimeoutCertificate,
) -> Result<(ChainInfoResponse, NetworkActions), WorkerError> {
// Check that the chain is active and ready for this timeout.
// Verify the certificate. Returns a catch-all error to make client code more robust.
self.initialize_and_save_if_needed().await?;
let (chain_epoch, committee) = self.chain.current_committee().await?;
certificate.check(&committee)?;
if self
.chain
.tip_state
.get()
.already_validated_block(certificate.inner().height())?
{
return Ok((self.chain_info_response().await?, NetworkActions::default()));
}
ensure!(
certificate.inner().epoch() == chain_epoch,
WorkerError::InvalidEpoch {
chain_id: certificate.inner().chain_id(),
chain_epoch,
epoch: certificate.inner().epoch()
}
);
let old_round = self.chain.manager.current_round();
self.chain
.manager
.handle_timeout_certificate(certificate, self.storage.clock().current_time());
self.save().await?;
let actions = self.create_network_actions(Some(old_round)).await?;
Ok((self.chain_info_response().await?, actions))
}
/// Tries to load all blobs published in this proposal.
///View on GitHub (pinned to 6c226ddcb3)
Solutions
- Bring the node's admin-chain state up to date so chain_epoch matches the certificate, then retry
- Re-create the timeout certificate under the current epoch's committee
- During epoch migrations, order operations so epoch-creating admin blocks are processed before dependent certificates are submitted
Defensive patterns
Strategy: validation
Validate before calling
// Compare epochs before submitting a timeout certificate.
let info = client.chain_info(chain_id).await?;
let (chain_epoch, _) = current_committee_of(&info)?;
if certificate.inner().epoch() != chain_epoch {
// Sync admin-chain state first, or mint a fresh certificate this epoch.
return sync_admin_chain(&client).await;
}
client.submit_timeout_certificate(chain_id, certificate).await?; Type guard
fn is_invalid_epoch(e: &WorkerError) -> bool {
matches!(e, WorkerError::InvalidEpoch { .. })
} Prevention
- During committee migrations, process epoch-creating admin blocks on every node before submitting dependent certificates
- Never replay certificates captured under a previous epoch
- Surface the expected-vs-found epochs from the error to pinpoint which side is stale
When it happens
Trigger: Submitting a TimeoutCertificate minted under the previous committee after the chain already transitioned to a new epoch (or the certificate is from a future epoch the chain has not entered).
Common situations: Committee/epoch migration in progress; the receiving validator has not yet processed the latest admin-chain blocks that create the new epoch; replaying certificates captured before an epoch change; uneven admin-chain sync across validators.
Related errors
- InvalidCommitteeEpoch
- no signer found for owner ${owner}
- MetaMask is not connected with the requested owner: ${owner}
- CannotRejectMessage
- Certificate justification commitment does not match its just
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/ba761a1b29485a31.
Report an issue: GitHub.