{"record":{"id":"4fb700d26f21df16","repo":"astrid-runtime/astrid","slug":"the-previous-astrid-daemon-pid-pid-did-not-exi","errorCode":null,"errorMessage":"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.","messagePattern":"The previous Astrid daemon \\(PID (.+?)\\) did not exit even after SIGKILL; refusing to start a second daemon while the lock may still be held\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/astrid-cli/src/commands/restart.rs","lineNumber":68,"sourceCode":"    // released the singleton/state-db lock — a wedged daemon `handle_stop`\n    // could not reach over the socket may still be alive. Spawning now would\n    // race the new daemon against a held lock and fail with \"Database … is\n    // already locked\". Verify the recorded PID is actually gone (signalling it\n    // if it survived `handle_stop`'s own kill path) before spawning.\n    let pid_path = socket_client::pid_path();\n    if let Some((pid, _exe)) = daemon_control::read_pid_file(&pid_path)\n        && daemon_control::is_process_alive(pid)\n    {\n        eprintln!(\n            \"{}\",\n            Theme::warning(&format!(\n                \"A process holding the recorded daemon PID {pid} is still alive after stop — \\\n                 verifying before restart.\"\n            ))\n        );\n        match daemon_control::terminate_orphan(&pid_path).await {\n            daemon_control::KillOutcome::StillAlive => {\n                anyhow::bail!(\n                    \"The previous Astrid daemon (PID {pid}) did not exit even after SIGKILL; \\\n                     refusing to start a second daemon while the lock may still be held.\"\n                );\n            },\n            daemon_control::KillOutcome::Unverified(p) => {\n                // Fail-secure: a live PID we can't confirm is the daemon (likely\n                // PID reuse) is NOT killed. Don't blindly spawn either — if it\n                // *were* the daemon, the lock is still held and the spawn would\n                // fail on it anyway. Make the operator resolve it explicitly.\n                anyhow::bail!(\n                    \"Cannot confirm PID {p} is the Astrid daemon (possible PID reuse); \\\n                     refusing to auto-restart. Inspect PID {p} — if it is not Astrid, \\\n                     remove {} and retry.\",\n                    pid_path.display()\n                );\n            },\n            _ => {\n                let _ = std::fs::remove_file(&pid_path);","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/restart.rs#L50-L86","documentation":"`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.","triggerScenarios":"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.","commonSituations":"Daemon wedged on unkillable I/O in containers with stale mounts, Docker/Kubernetes containers with paused PIDs, or a hung syscall during shutdown.","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"],"exampleFix":"# before (blind retry)\nastrid restart\n# after\nps -o pid,stat,wchan,cmd -p $(cat /path/to/astrid.pid)\n# resolve the stuck process, then\nastrid restart","handlingStrategy":"try-catch","validationCode":"pid=$(cat \"$PID_FILE\")\nif kill -0 \"$pid\" 2>/dev/null; then\n  echo \"PID $pid still alive; resolve before restart\" >&2\n  exit 1\nfi","typeGuard":null,"tryCatchPattern":"match daemon_control::terminate_orphan(&pid_path).await {\n    daemon_control::KillOutcome::StillAlive => {\n        eprintln!(\"old daemon unkillable; inspect with ps and resolve blocking I/O\");\n        std::process::exit(1);\n    }\n    _ => { /* safe to restart */ }\n}","preventionTips":["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"],"tags":["daemon","process-lifecycle","fail-safe"],"backgroundTag":"process-still-alive-after-kill","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}