Hmbown/CodeWhale · error

the cloud agent runner panicked

Error message

the cloud agent runner panicked

What it means

join_runner waits on a spawned cloud-agent runner thread via JoinHandle::join. A panic inside that thread surfaces as Err(()) from join, which is converted into "the cloud agent runner panicked". The real panic payload is discarded, so this indicates an internal crash in the runner thread, not a user-input problem.

Solutions

  1. Re-run the command; transient panics may not reproduce
  2. Check the job in the store — the code still reloads and prints the job after join fails
  3. Capture RUST_BACKTRACE=1 output to find the panic site in the runner
  4. Report the panic with the job id and CLI version; this is an internal invariant violation
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = run_with(...).await {
    if e.to_string().contains("runner panicked") {
        eprintln!("cloud agent crashed; job state may still be readable from the store");
    }
    return Err(e);
}

Prevention

When it happens

Trigger: The background thread spawned to run/monitor the cloud agent job panicked (unexpected None unwrap, assertion, index panic, or an explicit panic in runner code).

Common situations: A cloud job returns malformed data the runner doesn't expect, a network callback unwraps a failed request, or a version mismatch between CLI and cloud service trips an invariant.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/c49f08f73808f4d5. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/dispatch.rs:164

    let job_id = outcome_job_id(&outcome).unwrap_or_default();
    write_outcome(out, outcome)?;
    join_runner(out, &store, &job_id, runner)
}

/// The CLI stays attached to a confirmed run: the card prints immediately,
/// then the process waits for the runner so a paid sandbox is never
/// orphaned by an early exit. Ctrl-C exits the wait; the job stays recorded
/// and `--cancel` tears the sandbox down.
fn join_runner<W: Write>(
    out: &mut W,
    store: &CloudJobStore,
    id: &str,
    runner: Option<std::thread::JoinHandle<()>>,
) -> Result<()> {
    if let Some(runner) = runner {
        runner
            .join()
            .map_err(|_| anyhow::anyhow!("the cloud agent runner panicked"))?;
        if !id.is_empty()
            && let Ok(job) = store.load(id)
        {
            writeln!(out, "{}", format_job(&job))?;
        }
    }
    Ok(())
}

fn outcome_job_id(outcome: &DispatchOutcome) -> Option<String> {
    match outcome {
        DispatchOutcome::Proposal(job)
        | DispatchOutcome::Refused(job)
        | DispatchOutcome::Accepted(job) => Some(job.id.clone()),
    }
}

/// Newest jobs for the status card's receipts section (best effort — an

View on GitHub (pinned to 73e0f67d83)