linera-io/linera-protocol · error

should execute block with Transfer operations

Error message

should execute block with Transfer operations

What it means

supply_fungible_tokens (linera-client/src/client_context.rs:1311) pushes fungible-token Transfer operations in chunks of up to 1000 and calls ClientOutcome::expect on each chunk's result. As with error 422, execute_operations returns ClientOutcome and expect panics unless the chunk was Committed - i.e. the client hit WaitForTimeout (not the round leader) or Conflict (another block committed at the same height).

Source

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

            .unwrap()
            .unwrap()
            .owner
            .unwrap();
        // This should be enough to run the benchmark at 1M TPS for an hour.
        let amount = Amount::from_nanos(4);
        let operations: Vec<Operation> = key_pairs
            .iter()
            .map(|(chain_id, owner)| {
                fungible_transfer(application_id, *chain_id, default_key, *owner, amount)
            })
            .collect();
        let chain_client = self.make_chain_client(default_chain_id).await?;
        // Put at most 1000 fungible token operations in each block.
        for operation_chunk in operations.chunks(1000) {
            chain_client
                .execute_operations(operation_chunk.to_vec(), vec![])
                .await?
                .expect("should execute block with Transfer operations");
        }
        self.update_wallet_from_client(&chain_client).await?;

        Ok(())
    }
}

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Stop every other client or benchmark process using the same wallet/default chain, then re-run.
  2. Let pending blocks and the current round settle before invoking benchmark preparation.
  3. If embedding, loop on ClientOutcome: on WaitForTimeout sleep until the timeout timestamp and retry the same chunk; on Conflict resynchronize the chain and re-prepare the operations.
  4. Confirm the default chain has single ownership during the benchmark.

Example fix

// before
for operation_chunk in operations.chunks(1000) {
    chain_client
        .execute_operations(operation_chunk.to_vec(), vec![])
        .await?
        .expect("should execute block with Transfer operations");
}

// after
for operation_chunk in operations.chunks(1000) {
    loop {
        match chain_client
            .execute_operations(operation_chunk.to_vec(), vec![])
            .await?
        {
            ClientOutcome::Committed(_) => break,
            ClientOutcome::WaitForTimeout(t) => {
                tokio::time::sleep_until(t.timestamp.into()).await
            }
            ClientOutcome::Conflict(_) => { /* resync chain state, then retry chunk */ }
        }
    }
}
Defensive patterns

Strategy: retry

Type guard

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

Try / catch

for chunk in operations.chunks(1000) {
    loop {
        match chain_client.execute_operations(chunk.to_vec(), vec![]).await? {
            ClientOutcome::Committed(_) => break,
            ClientOutcome::WaitForTimeout(t) => tokio::time::sleep_until(t.timestamp.into()).await,
            ClientOutcome::Conflict(_) => { /* resync and retry the same chunk */ }
        }
    }
}

Prevention

When it happens

Trigger: prepare_for_benchmark reaching the token-distribution step while another process commits to the default chain, or while the client must wait out a round timeout before it may propose the next block. Each 1000-operation chunk must commit, so a single non-Committed outcome aborts the whole run.

Common situations: Concurrent benchmark runs sharing one wallet/default chain; a long-running client (e.g. a wallet service) still operating the chain during benchmark preparation; leadership rotation on jointly owned chains.

Related errors


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