gitbutlerapp/gitbutler · warning · anyhow::Error

command not found

Error message

command not found

What it means

Shown in the `but` TUI command mode: input typed after `:` (but command) or `!` (shell command) is shell-split with `shell_words::split` and the first token is spawned with `Command::new(binary)`. When the OS reports `ErrorKind::NotFound` — the binary is not on PATH or the path is wrong — the TUI pushes a transient 'command not found' message and keeps running; other spawn errors propagate with 'failed to start command'.

Source

Thrown at crates/but/src/command/legacy/status/tui/app/command_mode.rs:221

                        return Ok(());
                    }
                };
                let Some(binary) = args.next() else {
                    return Ok(());
                };
                let mut cmd = Command::new(binary);
                cmd.args(args);
                cmd
            }
        };

        cmd.current_dir(ctx.workdir_or_fail()?);

        let mut child = match cmd.spawn() {
            Ok(child) => child,
            Err(err) => {
                if err.kind() == std::io::ErrorKind::NotFound {
                    self.push_transient_error(anyhow!("command not found"));
                    return Ok(());
                } else {
                    return Err(err).context("failed to start command");
                }
            }
        };
        let status = child.wait()?;

        if !IN_TEST {
            out.prompt_single_line("\npress enter to continue...")?;
        }

        if status.success() {
            messages.extend([
                Message::EnterNormalModeAfterConfirmingOperation,
                Message::Reload(None, ReloadCause::Mutation),
            ]);
        } else {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Check the spelling of the command
  2. Verify the binary is reachable in that environment: run `!which <cmd>` from the TUI or echo $PATH from a shell that matches the launch environment
  3. Use an absolute path for the command (e.g. `!/usr/bin/git status`) to take PATH out of the equation
  4. Launch `but` from your normal shell (with full PATH) rather than from a GUI/app launcher

Example fix

# before: not on the TUI's PATH
!gitui

# after: absolute path, or fix PATH
!/opt/homebrew/bin/gitui
Defensive patterns

Strategy: validation

Validate before calling

let binary = args[0].as_ref();
if !binary.to_string_lossy().contains(std::path::MAIN_SEPARATOR_str())
    && which::which(binary).is_err()
{
    self.push_transient_error(anyhow!("command not found: {binary:?} (check PATH)"));
    return Ok(());
}

Type guard

fn is_not_found(err: &std::io::Error) -> bool {
    err.kind() == std::io::ErrorKind::NotFound
}

Try / catch

match cmd.spawn() {
    Ok(child) => { let _ = child.wait()?; }
    Err(err) if is_not_found(&err) => {
        self.push_transient_error(anyhow!("command not found"));
        return Ok(());
    }
    Err(err) => return Err(err).context("failed to start command"),
}

Prevention

When it happens

Trigger: Typing a shell command (`!...`) whose binary is not installed or not on the TUI process's PATH, e.g. `!gitx` with gitx missing, or `!./tool` from the wrong directory.

Common situations: Launching the TUI from a GUI launcher or minimal-PATH environment where usual shell PATH entries are absent; typo in the command name; tool simply not installed; relative path not resolved because cwd differs from expectation.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/a0244ae7061b2799. Report an issue: GitHub.