denisidoro/navi · error · anyhow

Unable to acquire stdin of finder

Error message

Unable to acquire stdin of finder

What it means

Thrown by `call` in src/finder/mod.rs (via `ask_if_should_import_all`) when spawning the external finder process (e.g. fzf) succeeded, but the child's stdin handle is absent (`child.stdin` is None). Without stdin the library cannot stream data to the finder, so it aborts.

Source

Thrown at src/finder/mod.rs:222

                let repo = match self {
                    Self::Fzf => "https://github.com/junegunn/fzf",
                    Self::Skim => "https://github.com/lotabout/skim",
                };
                eprintln!(
                    "navi was unable to call {cmd}.
                Please make sure it's correctly installed.
                Refer to {repo} for more info.",
                    cmd = &finder_str,
                    repo = repo
                );
                process::exit(33)
            }
        };

        let stdin = child
            .stdin
            .as_mut()
            .ok_or_else(|| anyhow!("Unable to acquire stdin of finder"))?;

        let mut writer: Box<&mut dyn Write> = Box::new(stdin);

        let return_value = stdin_fn(&mut writer).context("Failed to pass data to finder")?;

        let out = child.wait_with_output().context("Failed to wait for finder")?;

        let output = parse(out, finder_opts).context("Unable to get output")?;
        Ok((output, return_value))
    }
}

View on GitHub (pinned to f7330b9ad5)

Solutions

  1. Ensure the finder subprocess is configured with `Stdio::piped()` for stdin before spawning
  2. Run the command in an interactive terminal where the finder can attach to stdin/stdout
  3. Verify the configured finder binary (fzf/skim) is correctly installed and is the real interactive finder, not a wrapper that closes stdin
  4. Check that no other code path calls `.stdin.take()` on the child before this point

Example fix

// before
let child = Command::new(finder).stdout(Stdio::piped()).spawn()?;
// after
let child = Command::new(finder)
    .stdin(Stdio::piped())
    .stdout(Stdio::piped())
    .spawn()?;
Defensive patterns

Strategy: try-catch

Validate before calling

// before invoking the finder, ensure an interactive stdin is plausible
if !atty::is(atty::Stream::Stdin) {
    eprintln!("finder requires an interactive stdin (TTY)");
}

Try / catch

match result {
    Err(e) if e.to_string().contains("Unable to acquire stdin of finder") => {
        eprintln!("Run inside an interactive terminal with a TTY attached.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling a finder-backed operation that needs to pipe input to the spawned finder process when `Command::spawn()` produced a child with `stdin: None` — e.g. the child was spawned with stdin not piped, or stdin was already taken/consumed elsewhere.

Common situations: Running in an environment with no TTY/standard input available (CI, non-interactive shells, detached processes); the finder binary configured in settings cannot accept piped stdin; a prior call already drained the child's stdin.

Related errors


AI-assisted analysis of denisidoro/navi@f7330b9ad5 (2026-09-03). Data as JSON: /api/errors/326db7ec2e65ffa4. Report an issue: GitHub.