{"record":{"id":"cf465c804a459436","repo":"sigoden/aichat","slug":"tool-call-exit-with-exit-code","errorCode":null,"errorMessage":"Tool call exit with {exit_code}","messagePattern":"Tool call exit with (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/function.rs","lineNumber":282,"sourceCode":"    let prepend_path = bin_dirs\n        .iter()\n        .map(|v| format!(\"{}{PATH_SEP}\", v.display()))\n        .collect::<Vec<_>>()\n        .join(\"\");\n    envs.insert(\"PATH\".into(), format!(\"{prepend_path}{current_path}\"));\n\n    let temp_file = temp_file(\"-eval-\", \"\");\n    envs.insert(\"LLM_OUTPUT\".into(), temp_file.display().to_string());\n\n    #[cfg(windows)]\n    let cmd_name = polyfill_cmd_name(&cmd_name, &bin_dirs);\n    if *IS_STDOUT_TERMINAL {\n        println!(\"{}\", dimmed_text(&prompt));\n    }\n    let exit_code = run_command(&cmd_name, &cmd_args, Some(envs))\n        .map_err(|err| anyhow!(\"Unable to run {cmd_name}, {err}\"))?;\n    if exit_code != 0 {\n        bail!(\"Tool call exit with {exit_code}\");\n    }\n    let mut output = None;\n    if temp_file.exists() {\n        let contents =\n            fs::read_to_string(temp_file).context(\"Failed to retrieve tool call output\")?;\n        if !contents.is_empty() {\n            output = Some(contents);\n        }\n    };\n    Ok(output)\n}\n\n#[cfg(windows)]\nfn polyfill_cmd_name<T: AsRef<Path>>(cmd_name: &str, bin_dir: &[T]) -> String {\n    let cmd_name = cmd_name.to_string();\n    if let Ok(exts) = std::env::var(\"PATHEXT\") {\n        for name in exts.split(';').map(|ext| format!(\"{cmd_name}{ext}\")) {\n            for dir in bin_dir {","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/function.rs#L264-L300","documentation":"`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.","triggerScenarios":"The command configured for the LLM function (run by `run_instructions_fn` or `eval`) exits with a non-zero status.","commonSituations":"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.","solutions":["Run the configured command manually with the same args/env to see the underlying failure","Fix the command or its arguments in the function config","Make the tool script exit 0 while writing a structured error to the output temp file","Check required env vars/dependencies for the tool"],"exampleFix":"// before\nmy_tool = { command = \"mytool --strict\" }\n// after: tolerate expected failures in the script\nmy_tool = { command = \"mytool || echo '{\\\"error\\\": \\\"handled\\\"}'\" }","handlingStrategy":"try-catch","validationCode":"let status = std::process::Command::new(cmd)\n    .args(&args)\n    .status()?;\nif !status.success() {\n    eprintln!(\"tool command would fail with {}\", status.code().unwrap_or(-1));\n}","typeGuard":null,"tryCatchPattern":"match run_llm_function(cmd_name, cmd_args, envs).await {\n    Err(e) if e.to_string().starts_with(\"Tool call exit with\") => {\n        eprintln!(\"Tool subprocess failed; inspect command and env\");\n    }\n    other => other?,\n}","preventionTips":["Test each configured tool command manually before wiring it up","Make tool scripts exit 0 and encode failures in their JSON output","Verify env vars and dependencies the tool needs are present"],"tags":["shell","tool-calls","exit-code"],"backgroundTag":"command-not-found","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}