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

  1. Let the block timestamp default — the client derives it as at least the parent's timestamp and local time
  2. If setting it manually, first query the chain's current timestamp and use block.timestamp >= that value
  3. 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

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


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