astrid-runtime/astrid · error
shutdown stage daemon.shutdown_ack: rejected: {reason}
Error message
shutdown stage daemon.shutdown_ack: rejected: {reason} What it means
When `astrid stop` sends KernelRequest::Shutdown over the socket, the daemon should ACK with KernelResponse::Success. If it instead replies KernelResponse::Error(reason), the daemon refused to begin shutting down, and the CLI bails with the staged message naming the rejection reason so the user knows shutdown never started.
Source
Thrown at crates/astrid-cli/src/commands/daemon.rs:771
// Graceful path: the socket is present and serviceable.
// Deliberately bypass the selected-workspace check: stopping a daemon is
// the recovery path when that daemon belongs to another project/layout.
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,View on GitHub (pinned to affd8760f4)
Solutions
- Read the {reason} in the message — it states why the daemon refused.
- Retry `astrid stop` once the transient condition (load/lease) clears.
- If shutdown is persistently rejected, use `astrid restart`, which force-terminates via the identity-gated signal path.
- Check daemon logs for the handler that produced the rejection.
Example fix
// before $ astrid stop Error: shutdown stage daemon.shutdown_ack: rejected: capsules still loading // after $ sleep 5 && astrid stop # or: astrid restart to force-terminate
Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
fn is_shutdown_ack(resp: &KernelResponse) -> bool {
matches!(resp, KernelResponse::Success(_))
} Try / catch
match stop_daemon().await {
Err(e) if e.to_string().contains("daemon.shutdown_ack: rejected") => {
eprintln!("daemon refused shutdown: {e}; falling back to restart");
run(vec!["astrid", "restart"])?;
}
r => r?,
} Prevention
- Don't stop while capsules are loading or long tasks run; wait for idle first.
- Read the embedded rejection reason before escalating to forced restart.
- Keep daemon version aligned with CLI to avoid payload-validation rejections.
- If rejections repeat, inspect daemon logs for the shutdown handler's precondition.
When it happens
Trigger: stop_daemon's graceful path: the socket is present, connect_kernel_for_recovery succeeds, the Shutdown request is delivered, and the daemon responds with KernelResponse::Error(reason) instead of Success — e.g. it believes shutdown is invalid in its current state or an internal precondition failed.
Common situations: Daemon mid-startup or mid-capsule-load refusing shutdown; daemon with active sessions/leases it considers non-interruptible; daemon in a degraded state whose shutdown handler returned a validation error; version skew where the daemon doesn't recognize the request payload.
Related errors
- daemon metadata lookup failed: {error}
- unexpected daemon metadata response: {other:?}
- daemon rejected status request: {message}
- shutdown stage daemon.process_reap: shutdown was acknowledge
- unexpected env list response: {response:?}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/313542a759839983.
Report an issue: GitHub.