denisidoro/navi · error
External command failed: {err}
Error message
External command failed:
{err} What it means
In `finder::parse`, after navi spawns an external fuzzy-finder process (fzf, sk, etc.) and reads its output, any exit code other than the handled ones (e.g. 0 for selection, 130 for user abort) triggers a panic including the child's stderr. This means the finder itself failed rather than the user simply canceling.
Source
Thrown at src/finder/mod.rs:45
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"fzf" => Ok(FinderChoice::Fzf),
"skim" => Ok(FinderChoice::Skim),
_ => Err("no match"),
}
}
}
fn parse(out: Output, opts: Opts) -> Result<String> {
let text = match out.status.code() {
Some(0) | Some(1) | Some(2) => {
String::from_utf8(out.stdout).context("Invalid utf8 received from finder")?
}
Some(130) => process::exit(130),
_ => {
let err = String::from_utf8(out.stderr)
.unwrap_or_else(|_| "<stderr contains invalid UTF-8>".to_owned());
panic!("External command failed:\n {err}")
}
};
let output = post::parse_output_single(text, opts.suggestion_type)?;
post::process(output, opts.column, opts.delimiter.as_deref(), opts.map)
}
impl FinderChoice {
fn check_fzf_version() -> Option<(u32, u32, u32)> {
let output = Command::new("fzf").arg("--version").output().ok()?.stdout;
let version_string = String::from_utf8(output).ok()?;
let version_parts: Vec<_> = version_string.split('.').collect();
if version_parts.len() == 3 {
let major = version_parts[0].parse().ok()?;
let minor = version_parts[1].parse().ok()?;
let patch = version_parts[2].split_whitespace().next()?.parse().ok()?;
Some((major, minor, patch))
} else {View on GitHub (pinned to f7330b9ad5)
Solutions
- Run the finder command manually (`fzf --version`, then the exact invocation) to see the underlying stderr in the panic message
- Update fzf to a recent version (navi relies on modern fzf features)
- Fix or remove broken options in navi's finder override settings
- Verify the configured finder binary is the real one (`which fzf`) and not a failing wrapper
Example fix
// before: navi panics with the finder's stderr $ navi External command failed: unknown option: --height40 // after $ fzf --version && fzf --height 40% # reproduce manually $ brew upgrade fzf # or apt install newer fzf $ navi
Defensive patterns
Strategy: try-catch
Validate before calling
use std::process::Command;
fn finder_available() -> bool {
Command::new("fzf").arg("--version").output()
.map(|o| o.status.success()).unwrap_or(false)
}
if !finder_available() {
eprintln!("fzf is missing or broken; install/upgrade it");
std::process::exit(1);
} Type guard
fn finder_output_ok(out: &std::process::Output) -> bool {
matches!(out.status.code(), Some(0) | Some(130))
} Try / catch
match navi::finder::call(opts) {
Ok(selection) => use_selection(selection),
Err(e) if e.to_string().contains("External command failed") => {
eprintln!("finder failed: {}", e);
eprintln!("check NAVI_FZF_OVERRIDES and your fzf version");
}
Err(e) => return Err(e),
} Prevention
- Keep fzf up to date (navi uses modern flags)
- Review overrides/options passed to the finder for typos
- Verify `which fzf` points at a real binary, not a failing alias
- Run navi in a proper TTY environment
When it happens
Trigger: `call` -> `parse` when the spawned finder exits with an unexpected status: fzf not actually installed (shell falls back or errors), fzf crashing, invalid finder options/flags passed via finder configuration, or stderr produced by an incompatible finder version.
Common situations: fzf version too old for the flags navi passes, a custom `NAVI_FZF_OVERRIDES`/finder config with broken options, an aliased or wrapper 'fzf' that fails, or terminal issues (no TTY) causing the finder to abort.
Related errors
- Failed to call: wget {} Output: {} Error: {}
- Failed to call: tldr {} Output: {} Error: {} Note: The cl
- No variables received from finder
- Unable to process value
AI-assisted analysis of denisidoro/navi@f7330b9ad5 (2026-09-03).
Data as JSON: /api/errors/db50f8623478c375.
Report an issue: GitHub.