astrid-runtime/astrid · critical
The previous Astrid daemon
Error message
The previous Astrid daemon (PID {pid}) did not exit even after SIGKILL; refusing to start a second daemon while the lock may still be held. What it means
`astrid restart` stops the running daemon, then verifies the old PID is gone before spawning a replacement. If `terminate_orphan` reports `StillAlive` — the process survived even SIGKILL — the CLI refuses to start a second daemon, because the stale process may still hold the daemon lock and a second instance would fail or corrupt state.
Solutions
- Inspect the PID: `ps -o pid,stat,wchan,cmd -p <pid>` to see why it won't die
- Resolve the blocking condition (unstick the mount, reap the zombie via its parent, wait for D-state I/O)
- If the PID is a different live process (reuse), remove the pid_path file and retry after confirming it is not Astrid
- Reboot or restart the container/host if the process is truly unkillable
Example fix
# before (blind retry) astrid restart # after ps -o pid,stat,wchan,cmd -p $(cat /path/to/astrid.pid) # resolve the stuck process, then astrid restart
Defensive patterns
Strategy: try-catch
Validate before calling
pid=$(cat "$PID_FILE") if kill -0 "$pid" 2>/dev/null; then echo "PID $pid still alive; resolve before restart" >&2 exit 1 fi
Try / catch
match daemon_control::terminate_orphan(&pid_path).await {
daemon_control::KillOutcome::StillAlive => {
eprintln!("old daemon unkillable; inspect with ps and resolve blocking I/O");
std::process::exit(1);
}
_ => { /* safe to restart */ }
} Prevention
- Monitor for D-state processes before restarts (ps -o stat)
- Clean pid files only after verifying the owning process is gone
- Restart daemons from supervision systems that reap children
- Avoid restarting while the host has stalled mounts
When it happens
Trigger: The recorded PID in the pid file still exists after stop+SIGKILL: process stuck in uninterruptible sleep (D state, e.g. blocked on NFS/FUSE I/O), zombie held by a non-reaping parent, or PID reuse where the new owner ignores signals.
Common situations: Daemon wedged on unkillable I/O in containers with stale mounts, Docker/Kubernetes containers with paused PIDs, or a hung syscall during shutdown.
Related errors
- Cannot confirm PID is the Astrid daemon (possible PID…
- Admin request timed out after
- an Astrid daemon appears to be running but its uplink is…
- an Astrid daemon appears to be running but its uplink is…
- an Astrid daemon is recorded as running (PID file) but its…
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/4fb700d26f21df16.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/restart.rs:68
// released the singleton/state-db lock — a wedged daemon `handle_stop`
// could not reach over the socket may still be alive. Spawning now would
// race the new daemon against a held lock and fail with "Database … is
// already locked". Verify the recorded PID is actually gone (signalling it
// if it survived `handle_stop`'s own kill path) before spawning.
let pid_path = socket_client::pid_path();
if let Some((pid, _exe)) = daemon_control::read_pid_file(&pid_path)
&& daemon_control::is_process_alive(pid)
{
eprintln!(
"{}",
Theme::warning(&format!(
"A process holding the recorded daemon PID {pid} is still alive after stop — \
verifying before restart."
))
);
match daemon_control::terminate_orphan(&pid_path).await {
daemon_control::KillOutcome::StillAlive => {
anyhow::bail!(
"The previous Astrid daemon (PID {pid}) did not exit even after SIGKILL; \
refusing to start a second daemon while the lock may still be held."
);
},
daemon_control::KillOutcome::Unverified(p) => {
// Fail-secure: a live PID we can't confirm is the daemon (likely
// PID reuse) is NOT killed. Don't blindly spawn either — if it
// *were* the daemon, the lock is still held and the spawn would
// fail on it anyway. Make the operator resolve it explicitly.
anyhow::bail!(
"Cannot confirm PID {p} is the Astrid daemon (possible PID reuse); \
refusing to auto-restart. Inspect PID {p} — if it is not Astrid, \
remove {} and retry.",
pid_path.display()
);
},
_ => {
let _ = std::fs::remove_file(&pid_path);View on GitHub (pinned to affd8760f4)