Open-Source Wikis

/

fd

/

Reference

/

Exit codes

sharkdp/fd

Exit codes

Defined by ExitCode in src/exit_codes.rs:

pub enum ExitCode {
    Success,
    HasResults(bool),
    GeneralError,
    KilledBySigint,
}

Mapping to process exit status

Variant Process exit status When fd returns this
Success 0 A normal search finished, regardless of whether anything matched (without -q/--quiet). All exec children also returned 0.
HasResults(true) 0 --quiet mode and at least one match.
HasResults(false) 1 --quiet mode and zero matches.
GeneralError 1 Anything went wrong: malformed pattern, invalid path, unreadable directory error in some cases, exec child returned non-zero, etc. The [fd error]: prefix on stderr typically explains why.
KilledBySigint 130 Ctrl-C was pressed. On Unix, fd re-installs the default SIGINT handler and re-raises the signal so callers see the conventional 128 + 2 = 130 status.

Exit-code merging

When fd runs multiple commands (-x / -X), each one produces an exit code. They are merged via merge_exitcodes:

pub fn merge_exitcodes(results: impl IntoIterator<Item = ExitCode>) -> ExitCode {
    if results.into_iter().any(ExitCode::is_error) {
        return ExitCode::GeneralError;
    }
    ExitCode::Success
}

Any non-zero result anywhere collapses the whole run to GeneralError. This applies to:

  • The receiver's own outcome.
  • Each -x worker thread.
  • Each -X CommandBuilder's success/failure.

Signal handling

ExitCode::exit does the right thing per variant:

pub fn exit(self) -> ! {
    #[cfg(unix)]
    if self == ExitCode::KilledBySigint {
        unsafe {
            if signal(Signal::SIGINT, SigHandler::SigDfl).is_ok() {
                let _ = raise(Signal::SIGINT);
            }
        }
    }
    process::exit(self.into())
}

The unsafe block re-raises SIGINT so calling shells see "fd was killed by SIGINT" rather than "fd exited 130 voluntarily" — a subtle but important distinction for shell scripts that check $?.

The Ctrl-C handler that produces KilledBySigint is installed in WorkerState::scan (src/walk.rs):

ctrlc::set_handler(move || {
    quit_flag.store(true, Ordering::Relaxed);
    if interrupt_flag.fetch_or(true, Ordering::Relaxed) {
        ExitCode::KilledBySigint.exit();
    }
}).unwrap();

The first SIGINT lets the walker drain gracefully. A second one short-circuits to immediate exit.

Notable consequences

  • fd 'pattern_with_no_matches' exits 0. Use -q if you want a non-zero exit status to signal "no match".
  • fd 'pat' -X false exits 1 (because false returned non-zero), even though fd's traversal succeeded.
  • Pressing Ctrl-C during a long search produces exit status 130 on Unix and 1 on Windows (Rust does not have a clean equivalent of raise(SIGINT) there).
  • A broken pipe in stdout (e.g., fd | head) is treated as GeneralError from the receiver's perspective; in practice this just means fd | head exits cleanly because the kernel SIGPIPEs fd before the receiver decides anything.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Exit codes – fd wiki | Factory