astrid-runtime/astrid · error
running daemon returned a malformed live capsule unload…
Error message
running daemon returned a malformed live capsule unload response
What it means
try_daemon_unload throws this when the raw response read from the daemon socket cannot be extracted into any recognized KernelResponse variant at all. This means the message on the response topic was malformed, of an unexpected type, or absent expected structure — a transport-level or schema-level anomaly rather than a daemon decision.
Solutions
- Restart the daemon and retry the unload to rule out a one-off corrupted message.
- Verify CLI and daemon versions match (same release) so the IPC schema agrees.
- Inspect the daemon logs and the socket traffic for a foreign publisher writing to the kernel response topic.
Defensive patterns
Strategy: retry
Validate before calling
// ensure only the expected daemon is bound to the proxy socket let reachable = daemon_socket_reachable().await;
Type guard
fn is_malformed_response(e: &anyhow::Error) -> bool {
e.to_string().contains("malformed live capsule unload response")
} Try / catch
match try_daemon_unload(...).await {
Err(e) if is_malformed_response(&e) => {
// retry once after reconnecting; then restart daemon
}
r => r?,
} Prevention
- Run CLI and daemon from the same build.
- Ensure no other process publishes on the kernel response topics.
- Reconnect the socket client before retrying after a malformed reply.
When it happens
Trigger: UnloadCapsule response read via read_until_topic does not parse as KernelResponse::Success or KernelResponse::Error (corrupt JSON, wrong topic payload, foreign message on the kernel_response topic, schema drift).
Common situations: Mixed CLI/daemon versions exchanging incompatible IPC payloads; another process publishing onto the same MQTT/socket topic; truncated or corrupted IPC message under load.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- daemon closed the response stream before the final marker
- daemon rejected status request
- daemon returned an unexpected status response
- detached FUSE service exceeded the startup response size
- FSKit callback probe correlation mismatch
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ef601ee658f7d15a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/capsule/live_load.rs:172
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)),
Err(crate::socket_client::WorkspaceConnectionError::Connect(_)) => Ok(None),
Err(crate::socket_client::WorkspaceConnectionError::Selection(error)) => Err(error),View on GitHub (pinned to affd8760f4)