astrid-runtime/astrid · error
Failed to connect to daemon: {e}
Error message
Failed to connect to daemon: {e} What it means
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.
Source
Thrown at crates/astrid-cli/src/commands/capsule_verb.rs:176
execute(&provider, &verb, &args).await
}
/// Ensure the daemon is up, connect, and fetch the CLI command registry.
async fn resolve_commands() -> Result<Vec<CommandInfo>> {
// These verbs require the daemon — auto-start it if needed.
daemon::ensure_daemon("capsule").await?;
let session = astrid_core::SessionId::from_uuid(Uuid::new_v4());
let source_id = session.0;
// Bind the connection to the active principal (and stamp it on the
// request) so the daemon scopes this management request to the invoking
// identity. A nil source with no principal falls back to the `default`
// (admin) principal — letting a non-admin enumerate capsule verbs under
// admin context, an RBAC bypass.
let caller = crate::principal::current();
let mut client = crate::socket_client::connect_for_workspace(session, caller.clone(), None)
.await
.map_err(|e| anyhow::anyhow!("Failed to connect to daemon: {e}"))?;
let req = astrid_core::kernel_api::KernelRequest::GetCommands;
let val = serde_json::to_value(req)?;
let msg = astrid_types::ipc::IpcMessage::new(
astrid_types::Topic::kernel_request("get_commands"),
astrid_types::ipc::IpcPayload::RawJson(val),
source_id,
)
.with_principal(caller.to_string());
client.send_message(msg).await?;
let raw = client
.read_until_topic(
astrid_types::Topic::kernel_response("get_commands").as_str(),
Duration::from_secs(10),
)
.await?;
match SocketClient::extract_kernel_response(&raw) {
Some(astrid_core::kernel_api::KernelResponse::Commands(cmds)) => Ok(cmds),View on GitHub (pinned to affd8760f4)
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.
Example fix
// before
let mut client = crate::socket_client::connect_for_workspace(session, caller.clone(), None)
.await
.map_err(|e| anyhow::anyhow!("Failed to connect to daemon: {e}"))?;
// after
let mut client = match crate::socket_client::connect_for_workspace(session, caller.clone(), None).await {
Ok(c) => c,
Err(e) => {
eprintln!("daemon not reachable ({e}); attempting local start...");
astrid_cli::commands::daemon::ensure_daemon().await?;
crate::socket_client::connect_for_workspace(session, caller.clone(), None).await?
}
}; Defensive patterns
Strategy: retry
Validate before calling
let socket = std::env::var("ASTRID_SOCKET").unwrap_or_else(|_| format!("{}/.astrid/daemon.sock", std::env::var("HOME")?));
if !std::path::Path::new(&socket).exists() {
return Err(anyhow!("daemon socket missing at {socket}; start the daemon first"));
} Try / catch
match result {
Err(e) if e.to_string().contains("Failed to connect to daemon") => {
ensure_daemon().await?; // start daemon, then retry connect once
retry_connect().await
}
other => other,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- capsules are installed, but connecting to the selected works
- failed to connect as '{principal}': {e}
- connection timed out after 5s
- daemon request: {e}
- Failed to connect to daemon. Check logs: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/bd47b0b0051c7e55.
Report an issue: GitHub.