linera-io/linera-protocol · error

should execute block with OpenChain operations

Error message

should execute block with OpenChain operations

What it means

execute_open_chains_operations (linera-client/src/client_context.rs:1286) submits a batch of OpenChain operations and calls ClientOutcome::expect on the result. ChainClient::execute_operations returns ClientOutcome<ConfirmedBlockCertificate>: Committed, WaitForTimeout (this client is not the leader of the current consensus round), or Conflict (another block was already committed at the next height). expect panics on anything except Committed, so this fires when the block could not be immediately committed during benchmark chain creation.

Source

Thrown at linera-client/src/client_context.rs:1307

        owners: Vec<AccountOwner>,
    ) -> Result<ConfirmedBlockCertificate, Error> {
        let operations: Vec<_> = owners
            .iter()
            .map(|owner| {
                let config = OpenChainConfig {
                    ownership: ChainOwnership::single_super(*owner),
                    account: AccountOwner::CHAIN,
                    balance,
                    application_permissions: Default::default(),
                };
                Operation::system(SystemOperation::OpenChain(config))
            })
            .collect();
        info!("Executing {} OpenChain operations", operations.len());
        Ok(chain_client
            .execute_operations(operations, vec![])
            .await?
            .expect("should execute block with OpenChain operations"))
    }

    /// Supplies fungible tokens to the chains.
    async fn supply_fungible_tokens(
        &mut self,
        key_pairs: &[(ChainId, AccountOwner)],
        application_id: ApplicationId,
    ) -> Result<(), Error> {
        let default_chain_id = self.default_chain();
        let default_key = self
            .wallet()
            .get(default_chain_id)
            .await
            .unwrap()
            .unwrap()
            .owner
            .unwrap();
        // This should be enough to run the benchmark at 1M TPS for an hour.

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Ensure only one client process operates the wallet/default chain at a time; stop stale benchmark processes before starting a new run.
  2. Wait for in-flight blocks and the current round to finalize, then retry the benchmark command.
  3. If embedding, replace the expect with a match on ClientOutcome that sleeps until RoundTimeout.timestamp and retries, or resynchronizes on Conflict.
  4. Keep the default chain under single ownership during benchmark setup so leadership is unambiguous.

Example fix

// before
Ok(chain_client
    .execute_operations(operations, vec![])
    .await?
    .expect("should execute block with OpenChain operations"))

// after
match chain_client.execute_operations(operations, vec![]).await? {
    ClientOutcome::Committed(certificate) => Ok(certificate),
    ClientOutcome::WaitForTimeout(timeout) => {
        tokio::time::sleep_until(timeout.timestamp.into()).await;
        // retry the batch
    }
    ClientOutcome::Conflict(certificate) => {
        // another block landed at this height: resynchronize and re-prepare
    }
}
Defensive patterns

Strategy: retry

Type guard

fn is_committed<T>(outcome: &ClientOutcome<T>) -> bool {
    matches!(outcome, ClientOutcome::Committed(_))
}

Try / catch

// Instead of .expect, handle all three outcomes; retry is the correct strategy:
loop {
    match chain_client.execute_operations(operations.clone(), vec![]).await? {
        ClientOutcome::Committed(cert) => return Ok(cert),
        ClientOutcome::WaitForTimeout(t) => tokio::time::sleep_until(t.timestamp.into()).await,
        ClientOutcome::Conflict(_) => { /* resynchronize chain, re-derive operations, retry */ }
    }
}

Prevention

When it happens

Trigger: Running benchmark preparation (prepare_for_benchmark / make_benchmark_chains flow) when the default chain's current round is led by another owner: a second client process operating the same chain/wallet, a leftover run still committing blocks, or multi-owner ownership where this client lost leadership.

Common situations: Two benchmark or client processes pointed at the same wallet or chain concurrently; re-running benchmarks before the previous run's blocks finished committing; chain ownership changed to multiple owners so WaitForTimeout becomes possible.

Related errors


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