sigoden/aichat · error

Tool call exit with

Error message

Tool call exit with {exit_code}

What it means

`run_llm_function` executes the function's command via `run_command` and treats a non-zero exit code as a tool failure, bubbling up 'Tool call exit with {exit_code}'. The tool subprocess ran but reported failure.

Solutions

  1. Run the configured command manually with the same args/env to see the underlying failure
  2. Fix the command or its arguments in the function config
  3. Make the tool script exit 0 while writing a structured error to the output temp file
  4. Check required env vars/dependencies for the tool

Example fix

// before
my_tool = { command = "mytool --strict" }
// after: tolerate expected failures in the script
my_tool = { command = "mytool || echo '{\"error\": \"handled\"}'" }
Defensive patterns

Strategy: try-catch

Validate before calling

let status = std::process::Command::new(cmd)
    .args(&args)
    .status()?;
if !status.success() {
    eprintln!("tool command would fail with {}", status.code().unwrap_or(-1));
}

Try / catch

match run_llm_function(cmd_name, cmd_args, envs).await {
    Err(e) if e.to_string().starts_with("Tool call exit with") => {
        eprintln!("Tool subprocess failed; inspect command and env");
    }
    other => other?,
}

Prevention

When it happens

Trigger: The command configured for the LLM function (run by `run_instructions_fn` or `eval`) exits with a non-zero status.

Common situations: Shell command in the function config fails (missing binary, bad flags, script error); environment vars passed via `envs` are wrong; the tool needs network/credentials that aren't present.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/cf465c804a459436. Report an issue: GitHub.

Appendix: source

Thrown at src/function.rs:282

    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") {
        for name in exts.split(';').map(|ext| format!("{cmd_name}{ext}")) {
            for dir in bin_dir {

View on GitHub (pinned to 82976d349a)