astrid-runtime/astrid · error
running daemon returned unload success without a status
Error message
running daemon returned unload success without a status
What it means
This error is thrown by try_daemon_unload when the running daemon replied with a KernelResponse::Success payload for an UnloadCapsule request, but the JSON payload lacks a 'status' string field. The CLI distinguishes 'unloaded' from 'not_loaded' via that field; without it the outcome is ambiguous, so the CLI refuses to guess and reports a protocol violation. It indicates a daemon/CLI version mismatch or a daemon bug rather than a problem with the capsule itself.
Source
Thrown at crates/astrid-cli/src/commands/capsule/live_load.rs:166
session_uuid,
);
client
.send_message(msg)
.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>(View on GitHub (pinned to affd8760f4)
Solutions
- Restart or upgrade the astrid daemon so it matches the CLI's expected IPC schema (Success payloads must include a string 'status' of 'unloaded' or 'not_loaded').
- Check daemon and CLI versions and reinstall them from the same release.
- Retry the unload after the daemon is updated; the file-state unload path can still be used if the daemon path keeps failing.
Example fix
// daemon side, before
Ok(json!({"capsule": id}))
// after
Ok(json!({"status": "unloaded", "capsule": id})) Defensive patterns
Strategy: type-guard
Validate before calling
// before trusting a daemon Success payload
fn has_unload_status(v: &serde_json::Value) -> bool {
matches!(v.get("status"), Some(s) if s.is_string())
} Type guard
fn unload_status(v: &serde_json::Value) -> Option<&str> {
v.get("status").and_then(serde_json::Value::as_str)
.filter(|s| matches!(*s, "unloaded" | "not_loaded"))
} Try / catch
match result {
Err(e) if e.to_string().contains("without a status") => {
// fall back to offline uninstall or prompt a daemon upgrade
}
Err(e) => return Err(e),
Ok(outcome) => handle(outcome),
} Prevention
- Pin CLI and daemon to the same release so the IPC schema matches.
- After daemon upgrades, smoke-test live load/unload before relying on it.
- Log full daemon responses when debugging protocol drift.
When it happens
Trigger: Issuing a live unload (astrid capsule live-unload) while a daemon is running; the daemon returns Success but its JSON body has no 'status' key (e.g. an older daemon or custom kernel build that omits the field).
Common situations: Daemon and CLI were upgraded independently so their IPC schema drifted; a third-party or patched daemon responds with an empty Success object; a proxy on the socket rewrites the response payload.
Related errors
- unexpected daemon response: {other:?}
- unexpected daemon response: {other:?}
- running daemon returned unknown unload status {other:?}
- unexpected daemon response: {other:?}
- daemon returned an unexpected status response: {other:?}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/6e28b6a737a7a607.
Report an issue: GitHub.