linera-io/linera-protocol · error

Expected balance: {expected_balance}, actual balance: {actua

Error message

Expected balance: {expected_balance}, actual balance: {actual_balance}

What it means

After the benchmark fired all fungible-token transfers, it polls each receiving app's balance with sleeps of 0+1+2+3+4 seconds (~10s total). If the balance never reaches the expected value by the last iteration (i==4), it bails with expected vs actual. This means transfers (or the messages carrying them) did not fully process/converge within the wait window — some transfers failed outright (counted as Failures earlier) or cross-chain messages were still in flight.

Source

Thrown at linera-service/src/benchmark.rs:246

        |((_, context, node_service), expected_balances)| {
            try_join_all(apps.iter().zip(expected_balances).map(
                |((_, sender_context, _), expected_balance)| async move {
                    if expected_balance == Amount::ZERO {
                        return Ok(()); // No transfers: The app won't be registered on this chain.
                    }
                    node_service.process_inbox(&context.default_chain).await?;
                    let app = FungibleApp(node_service.make_application(
                        &context.default_chain,
                        &sender_context.application_id,
                    )?);
                    for i in 0.. {
                        linera_base::time::timer::sleep(Duration::from_secs(i)).await;
                        let actual_balance = app.get_amount(&context.owner).await;
                        if actual_balance == expected_balance {
                            break;
                        }
                        if i == 4 {
                            bail!(
                                "Expected balance: {expected_balance}, actual balance: {actual_balance}",
                            );
                        }
                    }
                    assert_eq!(app.get_amount(&context.owner).await, expected_balance);
                    Ok(())
                },
            ))
        },
    ))
    .await?;

    Ok(())
}

struct FungibleApp(ApplicationWrapper<FungibleTokenAbi>);

impl FungibleApp {

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Check the 'Successes:/Failures:' lines printed just before: if failures > 0, the mismatch is failed transfers, not slowness — fix why they failed (funding, fees, contention).
  2. Reduce load (fewer wallets or transactions) so messages settle within the retry window.
  3. Give the deployment more resources / closer validators to speed up message processing.
  4. If you intentionally run heavy loads, raise the retry bound (i == 4) / sleep schedule in benchmark.rs before the final check.

Example fix

// before
for i in 0.. {
    linera_base::time::timer::sleep(Duration::from_secs(i)).await;
    let actual_balance = app.get_amount(&context.owner).await;
    if actual_balance == expected_balance { break; }
    if i == 4 { bail!("Expected balance: {expected_balance}, actual balance: {actual_balance}"); }
}

// after (heavy-load tuning): wider window and a diagnosed failure
for i in 0..10 {
    linera_base::time::timer::sleep(Duration::from_secs(i)).await;
    if app.get_amount(&context.owner).await == expected_balance { return Ok(()); }
}
bail!("balances did not converge in 45s; check the Failures count above for dropped transfers");
Defensive patterns

Strategy: retry

Validate before calling

// Before the balance check, confirm transfer success counts:
anyhow::ensure!(failures == 0, "{failures} transfers failed; balances cannot converge");

Try / catch

// The loop is the retry; on final mismatch, surface both balances plus the earlier
// Successes/Failures counts so the operator can tell 'slow' from 'lost'.

Prevention

When it happens

Trigger: Running benchmark_with_fungible where a subset of transfer operations fail (shown in the 'Failures:' log line) or process_inbox/synchronization lag exceeds ~10 seconds under load; then the final assert would also fail. The bail fires first once i reaches 4 with a mismatching balance.

Common situations: Overloaded benchmark nodes (too many wallets/transactions for the machine) causing slow block processing; network latency between the client and validator set; some transfers rejected (insufficient balance for fees) so the expected balance can never be reached.

Related errors


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