{"record":{"id":"da5a59d4a9927b75","repo":"astrid-runtime/astrid","slug":"model-discovery-thread-panicked","errorCode":null,"errorMessage":"model discovery thread panicked","messagePattern":"model discovery thread panicked","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/capsule/model_discovery.rs","lineNumber":334,"sourceCode":"/// before returning, keeping the prompt strictly sequential.\n///\n/// Returns `Err` on any failure (mirroring [`fetch_options`]); the caller\n/// maps that to a free-text fallback.\npub(crate) fn fetch_options_blocking(\n    opts: &OptionsFrom,\n    values: &HashMap<String, String>,\n) -> anyhow::Result<Vec<String>> {\n    std::thread::scope(|scope| {\n        scope\n            .spawn(|| {\n                let runtime = tokio::runtime::Builder::new_current_thread()\n                    .enable_all()\n                    .build()\n                    .context(\"failed to build discovery runtime\")?;\n                runtime.block_on(fetch_options(opts, values))\n            })\n            .join()\n            .map_err(|_| anyhow::anyhow!(\"model discovery thread panicked\"))?\n    })\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn vals(pairs: &[(&str, &str)]) -> HashMap<String, String> {\n        pairs\n            .iter()\n            .map(|(k, v)| ((*k).to_string(), (*v).to_string()))\n            .collect()\n    }\n\n    #[test]\n    fn resolve_template_substitutes_known_keys() {\n        let v = vals(&[(\"base_url\", \"https://api.openai.com\"), (\"api_key\", \"sk-x\")]);\n        assert_eq!(","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/capsule/model_discovery.rs#L316-L352","documentation":"fetch_options_blocking runs fetch_options on a dedicated thread with its own tokio runtime; if that thread panics, the join() returns Err and this error is raised instead. It converts an internal panic into a user-facing anyhow error so the caller can fall back to free-text input.","triggerScenarios":"A panic inside fetch_options or its dependencies (e.g. reqwest/tokio runtime bug, unwraps in parsing) while executing on the discovery thread.","commonSituations":"Upstream library panics on malformed responses; platform/runtime incompatibilities (e.g. sandboxed environments blocking thread spawn features); genuine bugs in model discovery code.","solutions":["Re-run the command; if reproducible, capture the panic backtrace (RUST_BACKTRACE=1) and report the underlying bug","Upgrade astrid-cli to get fixes in the discovery path","Bypass interactive discovery and enter the model name manually via the fallback","Check environment restrictions that could make the runtime builder or blocking thread fail"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let options = match fetch_options_blocking(opts, values) {\n    Ok(o) => o,\n    Err(e) if e.to_string().contains(\"thread panicked\") => {\n        eprintln!(\"model discovery failed; enter the model name manually.\");\n        vec![prompt_free_text()?\n    }\n    Err(e) => return Err(e),\n};","preventionTips":["Keep astrid-cli updated to pick up discovery-path fixes","Report reproducible panics with RUST_BACKTRACE=1 output","Treat interactive discovery as best-effort and always provide a manual entry fallback"],"tags":["panic","thread","runtime"],"backgroundTag":"internal-invariant-violation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}