linera-io/linera-protocol · error

Failed to unwrap shared context

Error message

Failed to unwrap shared context

What it means

Arc::try_unwrap on the shared ClientContext failed because at least one other Arc clone is still alive. After the benchmark finishes, main.rs needs exclusive ownership (&mut) to run wrap_up_benchmark, but the chain listener or another background task still holds a clone, so the strong count is > 1 at the unwrap point.

Source

Thrown at linera-service/src/cli/main.rs:943

                                }
                            })
                            .collect::<Result<_, _>>()?;

                        linera_client::benchmark::Benchmark::run_benchmark(
                            bps,
                            chain_clients.clone(),
                            generators,
                            transactions_per_block,
                            health_check_endpoints.clone(),
                            runtime_in_seconds,
                            delay_between_chains_ms,
                            chain_listener,
                            &shutdown_notifier,
                        )
                        .await?;

                        let mut context = std::sync::Arc::try_unwrap(shared_context)
                            .map_err(|_| anyhow::anyhow!("Failed to unwrap shared context"))?
                            .into_inner();
                        context
                            .wrap_up_benchmark(chain_clients, close_chains, wrap_up_max_in_flight)
                            .await?;
                    }

                    BenchmarkCommand::Multi {
                        options: benchmark_options,
                        processes,
                        faucet,
                        client_state_dir,
                        delay_between_processes,
                        cross_wallet_transfers,
                    } => {
                        let mut command = BenchmarkCommand::Single {
                            options: benchmark_options.clone(),
                        };
                        let faucet_client = cli_wrappers::Faucet::new(faucet.clone());

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Ensure every task holding an Arc<ClientContext> clone is joined (or aborted and awaited) before try_unwrap.
  2. Trigger shutdown_notifier, then await the listener task's JoinHandle so its clone is provably dropped.
  3. If the race is benign, retry try_unwrap briefly after signaling shutdown instead of failing immediately.
  4. Longer term, restructure wrap-up so it does not require exclusive ownership (e.g. message-passing to the task that owns the context).

Example fix

// before: unwrap races against tasks that still hold clones
let context = Arc::try_unwrap(shared_context)
    .map_err(|_| anyhow::anyhow!("Failed to unwrap shared context"))?
    .into_inner();

// after: signal shutdown, join the holder, then unwrap
shutdown_notifier.notify_one();
let shared_context = listener_handle.await?; // task returns its clone / drops it
let context = Arc::try_unwrap(shared_context)
    .expect("all clones dropped after tasks joined")
    .into_inner();
Defensive patterns

Strategy: retry

Validate before calling

if Arc::strong_count(&shared_context) != 1 {
    // another task still holds the ClientContext — signal and join it first
    shutdown_notifier.notify_one();
    listener_handle.abort();
}

Try / catch

let context = match Arc::try_unwrap(shared_context) {
    Ok(ctx) => ctx.into_inner(),
    Err(ctx) => {
        tracing::warn!("context still shared; waiting for holders to drop");
        shutdown_notifier.notify_one();
        let _ = listener_handle.await; // ensure the clone is dropped
        Arc::try_unwrap(ctx)
            .expect("all clones dropped after tasks joined")
            .into_inner()
    }
};

Prevention

When it happens

Trigger: The shutdown notifier fired but the listener task (or any task holding Arc<ClientContext>: notification handlers, query-subscription watchers) has not yet observed cancellation and dropped its clone — a race between shutdown signaling and task teardown.

Common situations: New background holders of the context added in newer versions that outlive the benchmark; overloaded machines where task teardown lags the shutdown signal; aborting on the unwrap path instead of joining tasks.

Related errors


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