{"record":{"id":"e4c2a20bc315ba4c","repo":"astrid-runtime/astrid","slug":"shutdown-stage-daemon-process-identity-pid-pid","errorCode":null,"errorMessage":"shutdown stage daemon.process_identity: PID {pid} is live but cannot be verified as Astrid; no markers were removed","messagePattern":"shutdown stage daemon\\.process_identity: PID (.+?) is live but cannot be verified as Astrid; no markers were removed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/astrid-cli/src/commands/daemon.rs","lineNumber":842,"sourceCode":"        )\n    );\n    let outcome = daemon_control::terminate_identity(&identity, pid_path).await;\n    confirm_kill_outcome(outcome)\n}\n\nfn confirm_kill_outcome(outcome: daemon_control::KillOutcome) -> Result<DaemonStopDisposition> {\n    match outcome {\n        daemon_control::KillOutcome::NotRunning => Ok(DaemonStopDisposition::AlreadyStopped),\n        daemon_control::KillOutcome::TermExited | daemon_control::KillOutcome::KilledExited => {\n            Ok(DaemonStopDisposition::Forced)\n        },\n        daemon_control::KillOutcome::StillAlive => {\n            anyhow::bail!(\n                \"shutdown stage daemon.process_reap: daemon did not exit after forced termination; the singleton lock may still be held\"\n            );\n        },\n        daemon_control::KillOutcome::Unverified(pid) => {\n            anyhow::bail!(\n                \"shutdown stage daemon.process_identity: PID {pid} is live but cannot be verified as Astrid; no markers were removed\"\n            );\n        },\n    }\n}\n\n/// Fence marker cleanup with the same singleton lock the daemon owns. Holding\n/// it through removal prevents a replacement daemon from publishing fresh\n/// markers between liveness proof and cleanup.\nasync fn cleanup_daemon_runtime(socket_path: &Path, pid_path: &Path) -> Result<()> {\n    let home = astrid_core::dirs::AstridHome::resolve()\n        .context(\"shutdown stage daemon.home_resolution\")?;\n    cleanup_daemon_runtime_for_home(&home, socket_path, pid_path).await\n}\n\nasync fn cleanup_daemon_runtime_for_home(\n    home: &astrid_core::dirs::AstridHome,\n    socket_path: &Path,","sourceCodeStart":824,"sourceCodeEnd":860,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/daemon.rs#L824-L860","documentation":"terminate_identity is identity-gated: before signalling a PID it verifies the process actually is Astrid (to protect an innocent process whose PID was recycled after a crash). If the recorded PID is alive but cannot be verified as an Astrid process, the CLI refuses to signal it and refuses to remove any runtime markers, bailing with the daemon.process_identity message so no unrelated process is ever killed.","triggerScenarios":"Orphan-path stop (or escalated graceful stop) where KillOutcome::Unverified(pid) is returned: the PID from the pid file is live, but its executable/cmdline/start-time markers do not match the recorded daemon identity — i.e. the PID was recycled by a different program.","commonSituations":"Long uptime after a daemon crash, so the OS reassigned the PID to another service; pid file left over from a previous boot (PID wraparound); multiple machines sharing a runtime dir over a network mount.","solutions":["Verify what the PID now belongs to (ps -p <pid> -o pid,cmd) — do NOT kill it manually if it is another service.","Remove the stale pid/socket markers (after confirming no real astrid daemon runs via pgrep -f astrid), then run `astrid start`.","Use `pgrep -f astrid-daemon` to locate the real daemon if one exists elsewhere.","Avoid sharing the Astrid runtime dir across hosts to prevent PID-space confusion."],"exampleFix":"// before\nError: shutdown stage daemon.process_identity: PID 4123 is live but cannot be verified as Astrid; no markers were removed\n// after\n$ ps -p 4123 -o cmd        # confirm it is NOT astrid\n$ pgrep -f astrid-daemon   # confirm no real daemon exists\n$ rm ~/.astrid/run/daemon.pid ~/.astrid/run/system.sock\n$ astrid start","handlingStrategy":"validation","validationCode":"// before stopping, confirm the recorded PID still is an astrid process\nlet pid = read_pid_file(&socket_client::pid_path())?;\nlet cmd = std::fs::read_to_string(format!(\"/proc/{pid}/cmdline\"))?.replace('\\0', \" \");\nif !cmd.contains(\"astrid\") {\n    eprintln!(\"PID {pid} was recycled by another process; clean markers instead of stopping\");\n}","typeGuard":"fn recorded_pid_is_astrid(pid: i32) -> bool {\n    std::fs::read_to_string(format!(\"/proc/{pid}/cmdline\"))\n        .map(|c| c.replace('\\0', \" \").contains(\"astrid\"))\n        .unwrap_or(false)\n}","tryCatchPattern":"match stop_daemon().await {\n    Err(e) if e.to_string().contains(\"cannot be verified as Astrid\") => {\n        eprintln!(\"PID recycled; cleaning stale markers instead of signalling\");\n        clean_runtime_markers()?;\n        run(vec![\"astrid\", \"start\"])?;\n    }\n    r => r?,\n}","preventionTips":["Never manually delete daemon.pid while the daemon is healthy — it enables PID-recycle confusion.","After a crash, clean markers promptly so PIDs aren't reused while stale files linger.","Check /proc/<pid>/cmdline before any manual kill of a recorded PID.","Don't share one Astrid runtime dir across machines or containers with independent PID spaces."],"tags":["daemon","pid","process-identity","safety"],"backgroundTag":"invalid-state-transition","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"}