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

  1. 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').
  2. Check daemon and CLI versions and reinstall them from the same release.
  3. 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

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/6e28b6a737a7a607. Report an issue: GitHub.