astrid-runtime/astrid · error
running daemon declined live capsule unload
Error message
running daemon declined live capsule unload: {reason} What it means
try_daemon_unload throws this when the running daemon explicitly responds with a KernelResponse::Error, carrying the daemon's reason for refusing the unload. The CLI surfaces that reason verbatim so the operator sees the daemon's actual denial (e.g. capability or permission failure) instead of a generic failure. This is a deliberate daemon-side rejection, not a transport or parse problem.
Solutions
- Read the daemon's reason embedded in the message and address it (e.g. stop the agent using the capsule before unloading).
- Restart the daemon if the refusal is caused by stale locks or sessions holding the capsule.
- Use --force-style removal paths or perform an offline uninstall if the daemon persistently refuses.
Defensive patterns
Strategy: try-catch
Validate before calling
// check daemon reachability and session permissions before unload assert!(daemon_socket_reachable().await, "daemon not reachable");
Type guard
fn is_daemon_denial(e: &anyhow::Error) -> bool {
e.to_string().contains("declined live capsule unload")
} Try / catch
if let Err(e) = try_daemon_unload(...).await {
if is_daemon_denial(&e) {
// inspect daemon reason, stop dependent agents, retry once
} else { return Err(e); }
} Prevention
- Stop agents using the capsule before issuing a live unload.
- Confirm the requesting principal has unload permission in daemon policy.
- Check for other sessions holding the capsule before unloading.
When it happens
Trigger: Sending KernelRequest::UnloadCapsule to a live daemon that responds KernelResponse::Error(reason) — for instance the daemon refuses because the capsule is busy, another session holds it, or the requester lacks permission.
Common situations: Unloading a capsule that is actively in use by a running agent; the daemon's policy denies unload for privileged capsules; stale daemon state references a capsule locked by a crashed session.
Related errors
- Admin request timed out after
- an Astrid daemon appears to be running but its uplink is…
- an Astrid daemon appears to be running but its uplink is…
- an Astrid daemon is recorded as running (PID file) but its…
- anyhow::anyhow!(error)
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/5ea0522f93a9ea55.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/capsule/live_load.rs:170
.await
.context("failed to send live capsule unload request")?;
let raw = client
.read_until_topic(response_topic.as_str(), std::time::Duration::from_secs(15))
.await
.context("running daemon did not confirm live capsule unload")?;
match crate::socket_client::SocketClient::extract_kernel_response(&raw) {
Some(KernelResponse::Success(data)) => {
match data.get("status").and_then(serde_json::Value::as_str) {
Some("unloaded") => Ok(LiveUnload::Unloaded),
Some("not_loaded") => Ok(LiveUnload::NotLoaded),
Some(other) => bail!("running daemon returned unknown unload status {other:?}"),
None => bail!("running daemon returned unload success without a status"),
}
},
Some(KernelResponse::Error(reason)) => {
bail!("running daemon declined live capsule unload: {reason}")
},
_ => bail!("running daemon returned a malformed live capsule unload response"),
}
}
async fn daemon_socket_reachable() -> bool {
let path = crate::socket_client::proxy_socket_path();
matches!(
astrid_core::local_transport::connect_outcome(&path).await,
Ok(astrid_core::local_transport::ConnectOutcome::Connected(_))
)
}
fn classify_live_client<T>(
result: crate::socket_client::WorkspaceConnectionResult<T>,
) -> anyhow::Result<Option<T>> {
match result {
Ok(client) => Ok(Some(client)),View on GitHub (pinned to affd8760f4)