uutils/coreutils · warning · io::Error
error-no-such-process
Error message
error-no-such-process
What it means
On Windows, process termination code constructs a NotFound io::Error with the localized 'error-no-such-process' message when it can be established that the target PID has already exited. probe_pid distinguishes 'already exited' from access denial using PROBE_ACCESS (PROCESS_SYNCHRONIZE).
Solutions
- Treat this NotFound error as success when the goal was termination
- Check existence first (probe the PID) before signaling
- Re-enumerate the process list to get a fresh PID
Example fix
// before
kill(pid)?;
// after
if let Err(e) = kill(pid) { if e.kind() != io::ErrorKind::NotFound { return Err(e); } } Defensive patterns
Strategy: try-catch
Validate before calling
// windows: check existence before signaling
fn pid_alive(pid: u32) -> bool {
std::process::Command::new("tasklist").args(["/FI", &format!("PID eq {pid}")]).output().map(|o| o.status.success()).unwrap_or(false)
} Try / catch
match kill(pid) {
Err(e) if e.kind() == std::io::ErrorKind::NotFound => { /* already exited: treat as success */ }
r => r?,
} Prevention
- Treat NotFound from kill as a benign outcome
- Avoid caching PIDs across long-running scripts
- Re-read the process list right before signaling
When it happens
Trigger: Calling kill/terminate on a PID that no longer exists, or a PID whose process exited between lookup and TerminateProcess (the subsequent ERROR_ACCESS_DENIED is disambiguated by probing with PROCESS_SYNCHRONIZE).
Common situations: Race conditions when killing short-lived processes; scripts computing PIDs from stale data; retry loops signaling processes that already finished.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
AI-assisted analysis of uutils/coreutils@92995feca2 (2026-09-20).
Data as JSON: /api/errors/8dd4467172931821.
Report an issue: GitHub.
Appendix: source
Thrown at src/uucore/src/lib/features/process/windows.rs:481
Disposition::Probe => match sys::wait_for_one(child.as_handle(), 0)? {
sys::WaitOutcome::TimedOut => Ok(()),
// The process has exited: the POSIX analog is ESRCH.
sys::WaitOutcome::Object(_) => Err(io::ErrorKind::NotFound.into()),
},
Disposition::Ignore => Ok(()),
Disposition::Interrupt | Disposition::Terminate => {
terminate_with_signal(child.as_handle(), signal)
}
}
}
const PROBE_ACCESS: u32 = PROCESS_SYNCHRONIZE;
// SYNCHRONIZE tells "already exited" apart from a real denial after a failed
// TerminateProcess (both report ERROR_ACCESS_DENIED).
const TERMINATE_ACCESS: u32 = PROCESS_TERMINATE | PROCESS_SYNCHRONIZE;
fn no_such_process() -> io::Error {
io::Error::new(io::ErrorKind::NotFound, translate!("error-no-such-process"))
}
/// A process handle becomes signaled when the process exits.
fn has_exited(handle: BorrowedHandle) -> io::Result<bool> {
Ok(matches!(
sys::wait_for_one(handle, 0)?,
sys::WaitOutcome::Object(_)
))
}
/// Request `SeDebugPrivilege` once per process; with it, opening a process
/// bypasses the target's security descriptor.
///
/// Silent and best-effort: only an elevated administrator's token holds the
/// privilege, and tokens without it are left untouched.
pub fn enable_debug_privilege() {
static REQUESTED: OnceLock<()> = OnceLock::new();
REQUESTED.get_or_init(|| {View on GitHub (pinned to 92995feca2)