astrid-runtime/astrid · error

shutdown stage daemon.shutdown_ack: unexpected response: {ot

Error message

shutdown stage daemon.shutdown_ack: unexpected response: {other:?}

What it means

stop_daemon expects an ACK (Success) or an explicit rejection (Error) for a Shutdown request; any other KernelResponse variant is a protocol anomaly. The CLI bails with the staged message and the debug form of the response, indicating the daemon (or something answering on its socket) does not speak the expected shutdown protocol.

Source

Thrown at crates/astrid-cli/src/commands/daemon.rs:774

    if socket_present && let Ok(client) = socket_client::connect_kernel_for_recovery().await {
        let mut client = client.with_timeout(Duration::from_secs(10));
        let response = client
            .request(KernelRequest::Shutdown {
                reason: Some("astrid stop".to_string()),
            })
            .await
            .context("shutdown stage daemon.shutdown_ack")?;
        let disposition = match response {
            KernelResponse::Success(_) => {
                // ACK only — confirm the process actually exits before
                // declaring success, and escalate if it wedged.
                confirm_graceful_stop(recorded, &socket_path, &pid_path).await?
            },
            KernelResponse::Error(reason) => {
                anyhow::bail!("shutdown stage daemon.shutdown_ack: rejected: {reason}")
            },
            other => {
                anyhow::bail!("shutdown stage daemon.shutdown_ack: unexpected response: {other:?}")
            },
        };
        cleanup_daemon_runtime(&socket_path, &pid_path).await?;
        return Ok(disposition);
    }

    // Orphan path: the socket is present but unreachable (hung/half-dead
    // daemon), OR the socket is already gone but a live recorded daemon is still
    // holding the lock. A clean shutdown request is impossible either way, so
    // signal the recorded PID (identity-gated) and clean up. Using the PID we
    // captured up front — not a re-read — closes the window where the daemon
    // deletes its own PID file mid-wedge.
    let outcome = match recorded.as_ref() {
        Some(identity) => daemon_control::terminate_identity(identity, &pid_path).await,
        None => daemon_control::KillOutcome::NotRunning,
    };
    let disposition = confirm_kill_outcome(outcome)?;
    cleanup_daemon_runtime(&socket_path, &pid_path).await?;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Run `astrid restart` to replace a version-skewed or foreign daemon with one matching the CLI.
  2. Verify which binary owns the recorded PID (ps -p <pid> -o cmdline) — an unrelated process may hold the socket.
  3. Reinstall so CLI and daemon share one build/protocol version.
  4. Check for another astrid home/AX_HOME pointing the CLI at a different daemon's socket.

Example fix

// before
Error: shutdown stage daemon.shutdown_ack: unexpected response: Status(DaemonStatus { ... })
// after
$ astrid restart   # align daemon protocol with CLI
$ astrid stop
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

fn is_ack_or_error(resp: &KernelResponse) -> bool {
    matches!(resp, KernelResponse::Success(_) | KernelResponse::Error(_))
}

Try / catch

match stop_daemon().await {
    Err(e) if e.to_string().contains("daemon.shutdown_ack: unexpected response") => {
        eprintln!("protocol mismatch on shutdown: {e}");
        run(vec!["astrid", "restart"])?; // respawn matching daemon
    }
    r => r?,
}

Prevention

When it happens

Trigger: Graceful stop path: connect_kernel_for_recovery succeeds and the Shutdown request is answered with a KernelResponse variant that is neither Success nor Error — e.g. Status, or a response type from a mismatched daemon version.

Common situations: CLI/daemon version skew after an upgrade with the old daemon still running; a different process bound to the socket path answering requests; a proxy/gateway intercepting and replying with its own variant set.

Related errors


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