{"record":{"id":"bd47b0b0051c7e55","repo":"astrid-runtime/astrid","slug":"failed-to-connect-to-daemon-e","errorCode":null,"errorMessage":"Failed to connect to daemon: {e}","messagePattern":"Failed to connect to daemon: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/capsule_verb.rs","lineNumber":176,"sourceCode":"    execute(&provider, &verb, &args).await\n}\n\n/// Ensure the daemon is up, connect, and fetch the CLI command registry.\nasync fn resolve_commands() -> Result<Vec<CommandInfo>> {\n    // These verbs require the daemon — auto-start it if needed.\n    daemon::ensure_daemon(\"capsule\").await?;\n\n    let session = astrid_core::SessionId::from_uuid(Uuid::new_v4());\n    let source_id = session.0;\n    // Bind the connection to the active principal (and stamp it on the\n    // request) so the daemon scopes this management request to the invoking\n    // identity. A nil source with no principal falls back to the `default`\n    // (admin) principal — letting a non-admin enumerate capsule verbs under\n    // admin context, an RBAC bypass.\n    let caller = crate::principal::current();\n    let mut client = crate::socket_client::connect_for_workspace(session, caller.clone(), None)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to connect to daemon: {e}\"))?;\n\n    let req = astrid_core::kernel_api::KernelRequest::GetCommands;\n    let val = serde_json::to_value(req)?;\n    let msg = astrid_types::ipc::IpcMessage::new(\n        astrid_types::Topic::kernel_request(\"get_commands\"),\n        astrid_types::ipc::IpcPayload::RawJson(val),\n        source_id,\n    )\n    .with_principal(caller.to_string());\n    client.send_message(msg).await?;\n    let raw = client\n        .read_until_topic(\n            astrid_types::Topic::kernel_response(\"get_commands\").as_str(),\n            Duration::from_secs(10),\n        )\n        .await?;\n    match SocketClient::extract_kernel_response(&raw) {\n        Some(astrid_core::kernel_api::KernelResponse::Commands(cmds)) => Ok(cmds),","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/capsule_verb.rs#L158-L194","documentation":"Wraps a failure from `socket_client::connect_for_workspace` when the CLI cannot establish a connection to the Astrid daemon over its IPC socket while resolving capsule commands. The library throws it because capsule verb enumeration requires a live daemon connection for the current workspace and principal. The message deliberately hides the underlying transport error's type but appends its display text ({e}). It also guards an RBAC path: a nil source with no principal would fall back to the admin principal, so connection identity is checked before connecting.","triggerScenarios":"Running a capsule verb command (via run_external/run_explicit -> resolve_commands) when the daemon is not running, the IPC socket file is stale or deleted, the socket path is wrong for the workspace, or the daemon rejects the connection due to principal/workspace identity mismatch.","commonSituations":"User runs an astrid capsule command before `astrid daemon start`; daemon crashed but socket file remains; ASTRID_HOME points to a different workspace than the running daemon; permission on the socket directory blocks connect.","solutions":["Start the daemon: `astrid daemon start` (or ensure_daemon) before running capsule verbs.","If the daemon should be running, check for a stale socket file at the workspace socket path and remove it, then restart the daemon.","Verify ASTRID_HOME / workspace configuration matches the workspace the daemon was started for.","Inspect the wrapped {e} text in the message for the concrete connect failure (permission denied, no such file, refused) and fix that cause.","Check that the principal resolves (no nil principal); re-authenticate if the principal/session is missing."],"exampleFix":"// before\nlet mut client = crate::socket_client::connect_for_workspace(session, caller.clone(), None)\n    .await\n    .map_err(|e| anyhow::anyhow!(\"Failed to connect to daemon: {e}\"))?;\n// after\nlet mut client = match crate::socket_client::connect_for_workspace(session, caller.clone(), None).await {\n    Ok(c) => c,\n    Err(e) => {\n        eprintln!(\"daemon not reachable ({e}); attempting local start...\");\n        astrid_cli::commands::daemon::ensure_daemon().await?;\n        crate::socket_client::connect_for_workspace(session, caller.clone(), None).await?\n    }\n};","handlingStrategy":"retry","validationCode":"let socket = std::env::var(\"ASTRID_SOCKET\").unwrap_or_else(|_| format!(\"{}/.astrid/daemon.sock\", std::env::var(\"HOME\")?));\nif !std::path::Path::new(&socket).exists() {\n    return Err(anyhow!(\"daemon socket missing at {socket}; start the daemon first\"));\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"Failed to connect to daemon\") => {\n        ensure_daemon().await?; // start daemon, then retry connect once\n        retry_connect().await\n    }\n    other => other,\n}","preventionTips":["Always call ensure_daemon / `astrid daemon start` before any capsule verb command in scripts.","Check `astrid daemon status` as a preflight in CI pipelines.","Pin ASTRID_HOME per workspace so socket paths are deterministic.","Treat stale socket files as a known failure mode and clean them on daemon shutdown."],"tags":["network","ipc","daemon","connection"],"backgroundTag":"connection-refused","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}