{"record":{"id":"c49f08f73808f4d5","repo":"Hmbown/CodeWhale","slug":"the-cloud-agent-runner-panicked","errorCode":null,"errorMessage":"the cloud agent runner panicked","messagePattern":"the cloud agent runner panicked","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cli/src/dispatch.rs","lineNumber":164,"sourceCode":"    let job_id = outcome_job_id(&outcome).unwrap_or_default();\n    write_outcome(out, outcome)?;\n    join_runner(out, &store, &job_id, runner)\n}\n\n/// The CLI stays attached to a confirmed run: the card prints immediately,\n/// then the process waits for the runner so a paid sandbox is never\n/// orphaned by an early exit. Ctrl-C exits the wait; the job stays recorded\n/// and `--cancel` tears the sandbox down.\nfn join_runner<W: Write>(\n    out: &mut W,\n    store: &CloudJobStore,\n    id: &str,\n    runner: Option<std::thread::JoinHandle<()>>,\n) -> Result<()> {\n    if let Some(runner) = runner {\n        runner\n            .join()\n            .map_err(|_| anyhow::anyhow!(\"the cloud agent runner panicked\"))?;\n        if !id.is_empty()\n            && let Ok(job) = store.load(id)\n        {\n            writeln!(out, \"{}\", format_job(&job))?;\n        }\n    }\n    Ok(())\n}\n\nfn outcome_job_id(outcome: &DispatchOutcome) -> Option<String> {\n    match outcome {\n        DispatchOutcome::Proposal(job)\n        | DispatchOutcome::Refused(job)\n        | DispatchOutcome::Accepted(job) => Some(job.id.clone()),\n    }\n}\n\n/// Newest jobs for the status card's receipts section (best effort — an","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/cli/src/dispatch.rs#L146-L182","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Re-run the command; transient panics may not reproduce","Check the job in the store — the code still reloads and prints the job after join fails","Capture RUST_BACKTRACE=1 output to find the panic site in the runner","Report the panic with the job id and CLI version; this is an internal invariant violation"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = run_with(...).await {\n    if e.to_string().contains(\"runner panicked\") {\n        eprintln!(\"cloud agent crashed; job state may still be readable from the store\");\n    }\n    return Err(e);\n}","preventionTips":["Set RUST_BACKTRACE=1 when reproducing so the panic site is captured","Keep CLI and cloud service versions compatible","Avoid feeding untrusted/malformed job payloads into the runner","Treat occurrences as bugs to report — this path is not an expected failure mode"],"tags":["panic","threading","cloud-agent"],"backgroundTag":"thread-interrupted","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}