fix: prevent E2BIG error in Claude Code provider by always passing system prompt via stdin

- Modified runProcess to always pass system prompt via stdin on all platforms
- Previously only Windows used stdin for system prompt to avoid cmd.exe limits
- Now all platforms use stdin to avoid Linux/Mac ARG_MAX limits (~128KB)
- This fixes crashes when using Claude Code with Codebase Indexing active
- Updated tests to reflect the new unified behavior

Fixes #9759
This commit is contained in:
Roo Code 2025-12-02 22:45:52 +00:00
parent 9a1d7a673b
commit e76d64fea0
2 changed files with 16 additions and 29 deletions

View file

@ -139,7 +139,7 @@ describe("runClaudeCode", () => {
expect(typeof result[Symbol.asyncIterator]).toBe("function")
})
test("should handle platform-specific stdin behavior", async () => {
test("should always pass system prompt and messages via stdin", async () => {
const { runClaudeCode } = await import("../run")
const messages = [{ role: "user" as const, content: "Hello world!" }]
const systemPrompt = "You are a helpful assistant"
@ -158,7 +158,7 @@ describe("runClaudeCode", () => {
results.push(chunk)
}
// On Windows, should NOT have --system-prompt in args
// Should NOT have --system-prompt in args on any platform
const [, args] = mockExeca.mock.calls[0]
expect(args).not.toContain("--system-prompt")
@ -179,13 +179,12 @@ describe("runClaudeCode", () => {
results2.push(chunk)
}
// On non-Windows, should have --system-prompt in args
// Should NOT have --system-prompt in args on any platform
const [, args2] = mockExeca.mock.calls[0]
expect(args2).toContain("--system-prompt")
expect(args2).toContain(systemPrompt)
expect(args2).not.toContain("--system-prompt")
// Should only pass messages via stdin
expect(mockStdin.write).toHaveBeenCalledWith(JSON.stringify(messages), "utf8", expect.any(Function))
// Should always pass both system prompt and messages via stdin
expect(mockStdin.write).toHaveBeenCalledWith(expectedStdinData, "utf8", expect.any(Function))
})
test("should include model parameter when provided", async () => {

View file

@ -154,17 +154,10 @@ function runProcess({
maxOutputTokens,
}: ClaudeCodeOptions & { maxOutputTokens?: number }) {
const claudePath = path || "claude"
const isWindows = os.platform() === "win32"
// Build args based on platform
const args = ["-p"]
// Pass system prompt as flag on non-Windows, via stdin on Windows (avoids cmd length limits)
if (!isWindows) {
args.push("--system-prompt", systemPrompt)
}
args.push(
// Build args - no longer passing system prompt as command-line argument
const args = [
"-p",
"--verbose",
"--output-format",
"stream-json",
@ -173,7 +166,7 @@ function runProcess({
// Roo Code will handle recursive calls
"--max-turns",
"1",
)
]
if (modelId) {
args.push("--model", modelId)
@ -196,17 +189,12 @@ function runProcess({
timeout: CLAUDE_CODE_TIMEOUT,
})
// Prepare stdin data: Windows gets both system prompt & messages (avoids 8191 char limit),
// other platforms get messages only (avoids Linux E2BIG error from ~128KiB execve limit)
let stdinData: string
if (isWindows) {
stdinData = JSON.stringify({
systemPrompt,
messages,
})
} else {
stdinData = JSON.stringify(messages)
}
// Always pass both system prompt and messages via stdin to avoid E2BIG errors
// This prevents issues when system prompts are very large (e.g., with Codebase Indexing)
const stdinData = JSON.stringify({
systemPrompt,
messages,
})
// Use setImmediate to ensure process is spawned before writing (prevents stdin race conditions)
setImmediate(() => {