Hmbown/CodeWhale · error · io::Error

not found on PATH

Error message

{} not found on PATH

What it means

The dependencies::Gh-style tool wrapper's output() helper runs the external tool and captures stdout/stderr; if Self::command() cannot locate the tool binary on PATH it returns an io::Error (NotFound) formatted as '{type name} not found on PATH'. The type name in the message identifies which tool was missing.

Solutions

  1. Install the named tool (see type name in the message) and ensure it is on PATH
  2. Relaunch codewhale from a shell where `which <tool>` resolves
  3. Fix PATH in the launching environment (shell profile, systemd unit, IDE settings)

Example fix

// before
Git::output(&["status"], repo_dir)?  // Err: cratename::Git not found on PATH
// after
$ which git || apt install git
$ codewhale  # relaunched with git on PATH
Defensive patterns

Strategy: try-catch

Validate before calling

fn tool_on_path<T: Dependency>() -> bool { T::command().is_some() }

Type guard

fn tool_available(cmd: &mut std::process::Command) -> bool { cmd.args(["--version"]).output().map(|o| o.status.success()).unwrap_or(false) }

Try / catch

match Tool::output(&["arg"], cwd) {
    Err(e) if e.kind() == std::io::ErrorKind::NotFound => eprintln!("{}: install it and add to PATH", e),
    other => other?,
}

Prevention

When it happens

Trigger: Calling the output() convenience (e.g. tool.output(&["arg"], cwd)) for a tracked dependency whose executable is absent from PATH in the current process environment.

Common situations: Tools installed per-user (nvm/rustup shims) while codewhale runs under a different user or service; minimal containers; IDE-spawned processes inheriting a stripped PATH.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/e03bd5810a094919. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/dependencies.rs:354

    ///
    /// Callers should chain `.args(...)`, `.current_dir(...)`, and then
    /// call `.output()`, `.status()`, or `.spawn()`.
    fn command() -> Option<Command> {
        let spec = Self::resolve()?;
        let (program, fixed_args) = split_interpreter_spec(&spec);
        let mut cmd = Command::new(&program);
        crate::utils::suppress_console_window(&mut cmd);
        for arg in &fixed_args {
            cmd.arg(arg);
        }
        Some(cmd)
    }

    /// Convenience: run the tool with arguments in a working directory
    /// and return the captured output.
    fn output(args: &[&str], cwd: &std::path::Path) -> std::io::Result<std::process::Output> {
        let mut cmd = Self::command().ok_or_else(|| {
            std::io::Error::new(
                std::io::ErrorKind::NotFound,
                format!("{} not found on PATH", std::any::type_name::<Self>()),
            )
        })?;
        cmd.args(args).current_dir(cwd).output()
    }

    /// Convenience: run the tool with arguments and return only the
    /// exit status (discards stdout/stderr).
    #[allow(dead_code)]
    fn status(args: &[&str], cwd: &std::path::Path) -> std::io::Result<std::process::ExitStatus> {
        let mut cmd = Self::command().ok_or_else(|| {
            std::io::Error::new(
                std::io::ErrorKind::NotFound,
                format!("{} not found on PATH", std::any::type_name::<Self>()),
            )
        })?;
        cmd.args(args).current_dir(cwd).status()

View on GitHub (pinned to 433685b202)