astrid-runtime/astrid · error
Daemon error: {err}
Error message
Daemon error: {err} What it means
resolve_commands asks the daemon for its external command list (GetCommands). When the daemon replies with KernelResponse::Error(err), the CLI deliberately surfaces the daemon's own error message as 'Daemon error: {err}' instead of folding it into a generic unexpected-response error. Per the source comment this typically represents a capability or permission denial on the daemon side.
Source
Thrown at crates/astrid-cli/src/commands/capsule_verb.rs:198
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),
// Surface the daemon's own error (e.g. a capability/permission denial)
// instead of folding it into a generic "unexpected response".
Some(astrid_core::kernel_api::KernelResponse::Error(err)) => {
anyhow::bail!("Daemon error: {err}")
},
_ => anyhow::bail!("Daemon returned an unexpected response to GetCommands"),
}
}
/// Publish the run request and await + render the result.
async fn execute(provider: &str, verb: &str, args: &[String]) -> Result<ExitCode> {
let session = astrid_core::SessionId::from_uuid(Uuid::new_v4());
let source_id = session.0;
// Bind the connection to the active principal so the capsule verb runs
// under the invoking identity's context (VFS/KV/secrets), not the
// `default` (admin) principal a nil/unstamped message falls back to.
let caller = crate::principal::current();
let mut client =
match crate::socket_client::connect_for_workspace(session, caller.clone(), None).await {
Ok(c) => c,
Err(e) => {
eprintln!(View on GitHub (pinned to affd8760f4)
Solutions
- Read the embedded daemon reason and grant the required capability/permission for the caller in the daemon policy.
- Re-authenticate the session with the daemon and retry.
- Restart the daemon if its policy state is stale after an upgrade.
Defensive patterns
Strategy: try-catch
Validate before calling
// probe capabilities before resolving external verbs
let caps = client.request(KernelRequest::GetCapabilities).await?;
if !caps.allows_external_verbs() {
eprintln!("principal lacks capability for external capsule verbs");
} Type guard
fn is_daemon_denial(e: &anyhow::Error) -> bool {
e.to_string().starts_with("Daemon error:")
} Try / catch
match resolve_commands(...).await {
Err(e) if is_daemon_denial(&e) => {
// read the daemon reason; request the capability or re-authenticate
}
r => r,
} Prevention
- Configure required capabilities for principals that resolve external verbs.
- Re-authenticate daemon sessions after policy changes.
- Keep daemon policy changes versioned with your deployment.
When it happens
Trigger: Invoking a command that must resolve its verb list via the daemon (external capsule verbs) while the daemon denies GetCommands — e.g. the caller lacks the required capability, or the daemon rejects the session/workspace context.
Common situations: Running external capsule verbs without the daemon permission configured for your principal; daemon policy changes after an upgrade; expired or unauthenticated daemon session.
Related errors
- daemon rejected capsule metadata request: {message}
- daemon metadata lookup failed: {error}
- unexpected daemon metadata response: {other:?}
- daemon rejected capsule metadata request: {message}
- unexpected daemon response: {other:?}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/31c8484c09737eed.
Report an issue: GitHub.