{"record":{"id":"495c5e209a390205","repo":"aaif-goose/goose","slug":"goose-cli-main-thread-panicked","errorCode":null,"errorMessage":"goose-cli main thread panicked","messagePattern":"goose-cli main thread panicked","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/goose-cli/src/main.rs","lineNumber":52,"sourceCode":"fn main() -> Result<()> {\n    #[cfg(windows)]\n    enable_windows_vt_processing();\n\n    let handle = std::thread::Builder::new()\n        .name(\"goose-cli-main\".to_string())\n        .stack_size(8 * 1024 * 1024)\n        .spawn(|| {\n            let runtime = tokio::runtime::Builder::new_multi_thread()\n                .enable_all()\n                .build()\n                .expect(\"Failed to build Tokio runtime\");\n            runtime.block_on(run())\n        })\n        .map_err(|e| anyhow::anyhow!(\"Failed to spawn goose-cli main thread: {}\", e))?;\n\n    handle\n        .join()\n        .map_err(|_| anyhow::anyhow!(\"goose-cli main thread panicked\"))?\n}\n","sourceCodeStart":34,"sourceCodeEnd":54,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-cli/src/main.rs#L34-L54","documentation":"After spawning the main worker thread, main() joins it; handle.join() returns Err only when that thread panicked (crates/goose-cli/src/main.rs:51-53). The panic payload is discarded and replaced by this generic message, so the real cause is whatever panicked inside the thread — most commonly the `.expect(\"Failed to build Tokio runtime\")` or any panic inside run().","triggerScenarios":"Any panic in the spawned thread: Tokio runtime construction failing (expected panic), or a panic/unwrap failure anywhere down the CLI's run() path (agent setup, provider config, etc.). The default panic hook prints the panic message to stderr before join() returns.","commonSituations":"A bug or unreachable state in the CLI path panicking at startup; a broken environment (e.g., invalid provider/model config causing an unwrap) that makes run() panic; the actual reason is visible in stderr just above this error line.","solutions":["Read stderr just above this error: the standard panic message ('thread ... panicked at ...') names the real failing location and payload","Reproduce with RUST_BACKTRACE=1 to get a full backtrace of the panic","Fix the underlying panic (often a runtime-build or configuration failure) rather than this join() wrapper"],"exampleFix":"// before (payload discarded)\nhandle\n    .join()\n    .map_err(|_| anyhow::anyhow!(\"goose-cli main thread panicked\"))?\n\n// after (surface the panic payload)\nhandle\n    .join()\n    .map_err(|payload| anyhow::anyhow!(\"goose-cli main thread panicked: {:?}\", payload))?","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Rust: join() returns Result — surface the panic payload instead of discarding it\nmatch handle.join() {\n    Ok(result) => result,\n    Err(payload) => {\n        let reason = payload\n            .downcast_ref::<&str>()\n            .map(|s| s.to_string())\n            .or_else(|| payload.downcast_ref::<String>().cloned())\n            .unwrap_or_else(|| \"unknown panic\".into());\n        anyhow::bail!(\"goose-cli main thread panicked: {reason}\")\n    }\n}","preventionTips":["Run with RUST_BACKTRACE=1 during development to capture panic origins","Fix the underlying panic identified in stderr; the join error is only a wrapper","Prefer explicit error returns over unwrap/expect in startup paths"],"tags":["rust","panic","startup","diagnostics"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}