fix(coding-agent): improve bash tool error handling (#479)

- Validate working directory exists before spawning to provide clear error message
- Add spawn error handler to prevent uncaught exceptions when shell not found or cwd invalid
- Add tests for both error scenarios

Without these fixes, spawn errors (e.g., ENOENT from missing cwd or shell) would
cause uncaught exceptions that crash the entire agent session instead of being
returned as clean tool errors.

Co-authored-by: robinwander <robinwander@users.noreply.github.com>
This commit is contained in:
Robin 2026-01-05 17:48:10 -06:00 committed by GitHub
parent c6aa0407e7
commit 1432fd91d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 3 deletions

View file

@ -1,14 +1,15 @@
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { bashTool } from "../src/core/tools/bash.js";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { bashTool, createBashTool } from "../src/core/tools/bash.js";
import { editTool } from "../src/core/tools/edit.js";
import { findTool } from "../src/core/tools/find.js";
import { grepTool } from "../src/core/tools/grep.js";
import { lsTool } from "../src/core/tools/ls.js";
import { readTool } from "../src/core/tools/read.js";
import { writeTool } from "../src/core/tools/write.js";
import * as shellModule from "../src/utils/shell.js";
// Helper to extract text from content blocks
function getTextOutput(result: any): string {
@ -277,6 +278,27 @@ describe("Coding Agent Tools", () => {
/timed out/i,
);
});
it("should throw error when cwd does not exist", async () => {
const nonexistentCwd = "/this/directory/definitely/does/not/exist/12345";
const bashToolWithBadCwd = createBashTool(nonexistentCwd);
await expect(bashToolWithBadCwd.execute("test-call-11", { command: "echo test" })).rejects.toThrow(
/Working directory does not exist/,
);
});
it("should handle process spawn errors", async () => {
vi.spyOn(shellModule, "getShellConfig").mockReturnValueOnce({
shell: "/nonexistent-shell-path-xyz123",
args: ["-c"],
});
const bashWithBadShell = createBashTool(testDir);
await expect(bashWithBadShell.execute("test-call-12", { command: "echo test" })).rejects.toThrow(/ENOENT/);
});
});
describe("grep tool", () => {