uutils/coreutils · info · io::Error

error-no-such-process

Error message

error-no-such-process

What it means

On Windows, `no_such_process` constructs an `io::Error` with `ErrorKind::NotFound` carrying the localized "error-no-such-process" message. `probe_pid` opens the process with PROCESS_SYNCHRONIZE only; if the open fails with ERROR_ACCESS_DENIED (a real denial after a failed TerminateProcess) versus the process already having exited, this error signals the process does not exist.

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 325183372a)

Solutions

  1. Check the process still exists before signaling, and treat NotFound as success for kill semantics
  2. Refresh the PID from the source that spawned it
  3. Ignore this error in fire-and-forget termination logic

Example fix

// before
kill(pid).expect("kill failed");
// after
if let Err(e) = kill(pid) {
    if e.kind() != io::ErrorKind::NotFound {
        return Err(e);
    } // already exited: fine
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Windows: probe first
fn pid_exists(pid: u32) -> bool {
    // probe_pid returns Err(NotFound) if the process has exited
    probe::probe_pid(pid).is_ok()
}

Type guard

fn is_no_such_process(e: &std::io::Error) -> bool {
    e.kind() == std::io::ErrorKind::NotFound
        && e.to_string().contains("no such process")
}

Try / catch

if let Err(e) = kill(pid) {
    if !is_no_such_process(&e) { return Err(e); }
    // already exited: treat as success
}

Prevention

When it happens

Trigger: Calling `probe_pid` (and by extension kill/terminate paths) with a PID whose process has already exited, or a PID that never existed on the system.

Common situations: Killing a process that already terminated (race), stale PIDs from a previous session, PID reuse assumptions in scripts.

Related errors


AI-assisted analysis of uutils/coreutils@325183372a (2026-08-31). Data as JSON: /api/errors/8dd4467172931821. Report an issue: GitHub.