sigoden/aichat · error · anyhow::Error
Unable to run
Error message
Unable to run {cmd_name}, {err} What it means
run_llm_function shells out to an external command; if spawning fails (binary missing, permission denied, IO error) the underlying error is wrapped with the command name. Non-zero exits produce a separate 'Tool call exit with N' error instead.
Solutions
- Install the command or add its directory to PATH / the configured bin_dirs
- Verify the tool name spelling in the function definition
- Check file permissions (chmod +x) on the executable
Example fix
// before: cmd not installed // after $ cargo install the-tool # or: export PATH="$PATH:/path/to/tool/bin"
Defensive patterns
Strategy: try-catch
Validate before calling
fn tool_available(cmd: &str, bin_dirs: &[PathBuf]) -> bool {
which::which(cmd).is_ok() || bin_dirs.iter().any(|d| d.join(cmd).exists())
} Try / catch
match run_llm_function(f).await {
Err(e) if e.to_string().starts_with("Unable to run") => {
eprintln!("Tool not available: {e}"); install_hint();
}
other => other?,
} Prevention
- Check tool availability with `which`/`where` before invoking
- Include the tool's bin dir in PATH or the config's bin_dirs
- Pin tool installations in your environment setup scripts
When it happens
Trigger: run_llm_function invoked (via run_instructions_fn or eval) and run_command returns Err — the executable isn't found on PATH or in bin_dirs, or can't be executed.
Common situations: Tool command not installed; PATH missing the tool's bin dir on the current platform; Windows polyfill_cmd_name failing to locate a .cmd/.exe shim; insufficient execute permissions.
Related errors
AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09).
Data as JSON: /api/errors/d16db9a40d855eb5.
Report an issue: GitHub.
Appendix: source
Thrown at src/function.rs:280
bin_dirs.push(Config::functions_bin_dir());
let current_path = std::env::var("PATH").context("No PATH environment variable")?;
let prepend_path = bin_dirs
.iter()
.map(|v| format!("{}{PATH_SEP}", v.display()))
.collect::<Vec<_>>()
.join("");
envs.insert("PATH".into(), format!("{prepend_path}{current_path}"));
let temp_file = temp_file("-eval-", "");
envs.insert("LLM_OUTPUT".into(), temp_file.display().to_string());
#[cfg(windows)]
let cmd_name = polyfill_cmd_name(&cmd_name, &bin_dirs);
if *IS_STDOUT_TERMINAL {
println!("{}", dimmed_text(&prompt));
}
let exit_code = run_command(&cmd_name, &cmd_args, Some(envs))
.map_err(|err| anyhow!("Unable to run {cmd_name}, {err}"))?;
if exit_code != 0 {
bail!("Tool call exit with {exit_code}");
}
let mut output = None;
if temp_file.exists() {
let contents =
fs::read_to_string(temp_file).context("Failed to retrieve tool call output")?;
if !contents.is_empty() {
output = Some(contents);
}
};
Ok(output)
}
#[cfg(windows)]
fn polyfill_cmd_name<T: AsRef<Path>>(cmd_name: &str, bin_dir: &[T]) -> String {
let cmd_name = cmd_name.to_string();
if let Ok(exts) = std::env::var("PATHEXT") {View on GitHub (pinned to 82976d349a)