linera-io/linera-protocol · error · ChainError
Block timestamp {new} must not be earlier than the parent bl
Error message
Block timestamp {new} must not be earlier than the parent block's timestamp {parent} What it means
execute_block (linera-chain/src/chain.rs:1311-1318) rejects a block whose timestamp is earlier than the chain's current timestamp, read from system.progress — the parent block's timestamp. Timestamps must be monotonically non-decreasing along a chain; a child cannot be timestamped before its parent.
Source
Thrown at linera-chain/src/chain.rs:1312
execution: BlockExecution,
) -> Result<
(
ProposedBlock,
BlockExecutionOutcome,
ResourceTracker,
HashSet<ChainId>,
),
ChainError,
> {
assert_eq!(
block.chain_id,
self.execution_state.context().extra().chain_id()
);
self.initialize_if_needed(local_time).await?;
let chain_timestamp = self.execution_state.system.progress.get().timestamp;
ensure!(
chain_timestamp <= block.timestamp,
ChainError::InvalidBlockTimestamp {
parent: chain_timestamp,
new: block.timestamp
}
);
ensure!(!block.transactions.is_empty(), ChainError::EmptyBlock);
ensure!(
block.published_blob_ids()
== published_blobs
.iter()
.map(|blob| blob.id())
.collect::<BTreeSet<_>>(),
ChainError::InternalError("published_blobs mismatch".to_string())
);
if *self.execution_state.system.closed.get() {View on GitHub (pinned to 6c226ddcb3)
Solutions
- Let the block timestamp default — the client derives it as at least the parent's timestamp and local time
- If setting it manually, first query the chain's current timestamp and use block.timestamp >= that value
- If caused by local clock skew, sync the machine's clock (NTP) and retry
Example fix
// before: explicit timestamp that may predate the parent
let block = ProposedBlock { timestamp: my_chosen_ts, .. };
// after: clamp to the chain's current (parent) timestamp
let chain_ts = client.chain_info(chain_id).await?.info.system_timestamp;
let block = ProposedBlock { timestamp: my_chosen_ts.max(chain_ts), .. }; Defensive patterns
Strategy: validation
Validate before calling
// Before proposing, clamp the timestamp to the chain's current one:
let chain_ts = client.chain_info(chain_id).await?.info.system_timestamp; // parent's timestamp
if block.timestamp < chain_ts {
block.timestamp = chain_ts; // or chain_ts.max(local_time)
} Try / catch
match result {
Err(ChainError::InvalidBlockTimestamp { parent, new }) => {
// set block.timestamp = parent (or later) and re-propose
}
other => other?,
} Prevention
- Use the client's default timestamp derivation instead of setting one
- Keep proposer clocks NTP-synced to avoid drifting behind the chain
- In tests, base timestamps on the chain's last timestamp, not constants
When it happens
Trigger: Explicitly setting ProposedBlock.timestamp below the parent's timestamp; a client machine whose clock lags behind the timestamp of the last confirmed block; test code reusing fixed timestamps while the chain has advanced past them.
Common situations: Overriding timestamps instead of using the client default (which computes max(parent timestamp, local time)); VM clock drift on the proposing node; deterministic-test fixtures with hardcoded times replayed against advanced chains.
Related errors
- Chain is expecting a next block at height {expected_block_he
- The previous block hash of a new block should match the last
- Incoming message bundle in block proposed to {chain_id} has
- Empty blocks are not allowed
- Missing operations or messages from mandatory applications:
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/c44e2ebe4b9381c4.
Report an issue: GitHub.