{"record":{"id":"72da576ee2ce41d8","repo":"neondatabase/neon","slug":"failed-to-send-signal-to-process-with-pid-pid","errorCode":null,"errorMessage":"Failed to send signal to process with pid {pid}: {err}","messagePattern":"Failed to send signal to process with pid (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"control_plane/src/background_process.rs","lineNumber":395,"sourceCode":"{\n    match status_check().await {\n        Ok(true) => match pid_file::read(pid_file_to_check)? {\n            PidFileRead::NotExist => Ok(false),\n            PidFileRead::LockedByOtherProcess(pid_in_file) => Ok(pid_in_file == pid),\n            PidFileRead::NotHeldByAnyProcess(_) => Ok(false),\n        },\n        Ok(false) => Ok(false),\n        Err(e) => anyhow::bail!(\"process failed to start: {e}\"),\n    }\n}\n\npub(crate) fn process_has_stopped(pid: Pid) -> anyhow::Result<bool> {\n    match kill(pid, None) {\n        // Process exists, keep waiting\n        Ok(_) => Ok(false),\n        // Process not found, we're done\n        Err(Errno::ESRCH) => Ok(true),\n        Err(err) => anyhow::bail!(\"Failed to send signal to process with pid {pid}: {err}\"),\n    }\n}\n","sourceCodeStart":377,"sourceCodeEnd":398,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/control_plane/src/background_process.rs#L377-L398","documentation":"process_has_stopped probes liveness with signal 0 via kill(pid, None): Ok means still alive, ESRCH means gone, and any other errno raises this error. In practice it is EPERM — the polling user lacks permission to signal the pid — which makes the stop loop abort instead of continuing to wait. It is the read-only twin of the stop_process signal error.","triggerScenarios":"Calling stop_process/wait_until_stopped when the target pid belongs to a different UID than the caller (process started under root, stopped under a normal user or vice versa). Also possible on systems with LSM policies denying signal-0 probes.","commonSituations":"Mixed-privilege workflows: one `neon_local` invocation under sudo, a later stop without it; CI containers where the compute process runs as another user; reusing an env directory across different accounts.","solutions":["Check ownership: `ps -o user= -p <pid>`; stop the process as the user that started it (e.g. prefix the stop command with sudo).","Kill the leftover process directly as the correct user (`sudo kill <pid>`), then let stop_process see ESRCH and finish cleanly.","Standardize one account for starting and stopping all processes in a neon_local env.","If the pid is long gone but the error persists, verify with `kill -0 <pid>` from the same shell to reproduce the permission denial."],"exampleFix":"// before\nwait_until_stopped(process_name, pid)?; // EPERM on signal-0 probe\n\n// after\n// probe with the same privileges used to start the process\nlet alive = std::process::Command::new(\"sudo\")\n    .args([\"kill\", \"-0\", &pid.to_string()])\n    .status()?.success();\nif !alive { /* stopped */ }","handlingStrategy":"try-catch","validationCode":"// before waiting, confirm signal permission with a signal-0 probe\nmatch kill(pid, None) {\n    Err(nix::errno::Errno::EPERM) => anyhow::bail!(\"no permission to probe pid {pid}; stop as the owning user\"),\n    _ => {},\n}","typeGuard":null,"tryCatchPattern":"match wait_until_stopped(process_name, pid) {\n    Err(e) if e.to_string().contains(\"Failed to send signal to process\") => {\n        // permission problem, not a hang: switch privileges and re-probe\n        std::process::Command::new(\"sudo\").args([\"kill\", \"-0\", &pid.to_string()]).status()?;\n        Ok(())\n    }\n    other => other,\n}","preventionTips":["Run stop flows under the same user that started the processes.","Treat EPERM on signal-0 probes as a privilege mismatch, not a liveness signal.","Automate env teardown in the same account/context used for setup."],"tags":["rust","neon","control-plane","background-process","signal","permissions","eperm"],"backgroundTag":"kill-permission-denied","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}