{"record":{"id":"df1d80e1daf440fb","repo":"astrid-runtime/astrid","slug":"headless-configuration-error-state-was-poisoned","errorCode":null,"errorMessage":"headless configuration error state was poisoned","messagePattern":"headless configuration error state was poisoned","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/capsule/install_finish.rs","lineNumber":48,"sourceCode":"            let vars = prompt.vars.clone();\n            let errors = std::sync::Arc::clone(&headless_errors);\n            handle.spawn(async move {\n                headless_elicit_handler(receiver, bus_for_handler, vars, errors).await;\n            })\n        } else {\n            handle.spawn(async move {\n                cli_elicit_handler(receiver, bus_for_handler).await;\n            })\n        }\n    });\n    let result = f(opts, event_bus.clone());\n    if let Some(task) = elicit_task {\n        task.abort();\n    }\n    drop(event_bus);\n    let errors = headless_errors\n        .lock()\n        .map_err(|_| anyhow::anyhow!(\"headless configuration error state was poisoned\"))?;\n    if !errors.is_empty() {\n        bail!(\n            \"non-interactive capsule configuration failed: {}\",\n            errors.join(\"; \")\n        );\n    }\n    result\n}\n\n/// Validate install output, surface diagnostics, and persist manual install\n/// configuration before returning the installed capsule identity.\npub(super) fn finish_install(\n    output: &InstallOutput,\n    home: &AstridHome,\n    principal: &astrid_core::PrincipalId,\n    prompt: &ManualInstallOptions,\n) -> anyhow::Result<InstalledCapsuleOutcome> {\n    let batch = BATCH_MODE.load(std::sync::atomic::Ordering::Relaxed);","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/capsule/install_finish.rs#L30-L66","documentation":"During non-interactive (`seal`) installs, configuration errors are accumulated in a shared `Mutex`-guarded `headless_errors` list and reported at the end by `run_with_elicit`. This error is thrown when the mutex is poisoned — i.e. another thread panicked while holding the lock — so the collected error list cannot be read reliably.","triggerScenarios":"A worker thread/task that shares `headless_errors` panics while holding the lock (e.g. a bug in a config callback), then the finishing code in `run_with_elicit` calls `.lock()` on the poisoned mutex.","commonSituations":"A panic inside elicit/config collection tasks during headless installs (often from `.unwrap()` in a prompt formatter or malformed manifest triggering an unexpected panic); only surfaces after a prior panic, so check earlier log output.","solutions":["Inspect earlier logs for the panic that poisoned the mutex — that root panic is the real failure","Retry the install; if reproducible, file a bug with the capsule manifest and command line","Update the CLI/capsule — panics in config collection are bugs to fix at the source"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"poisoned\") => {\n        // Root cause is an earlier panic; re-run with backtrace and inspect logs.\n        eprintln!(\"Headless install crashed earlier (poisoned lock); re-run with RUST_BACKTRACE=1.\");\n    }\n    other => other?,\n}","preventionTips":["Treat this as a symptom: always investigate the preceding panic in logs","Run headless installs with RUST_BACKTRACE=1 to capture root panics","Keep astrid-cli updated; panics in config collection are upstream bugs"],"tags":["concurrency","mutex","poisoned-lock","headless","capsule"],"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"}