Skip to content

Commit 83486e1

Browse files
committed
fix: improve Claude Code ENOENT error handling with installation guidance
- Add helpful error messages when Claude Code executable is not found - Provide platform-specific installation instructions - Handle ENOENT errors both during process spawn and execution - Guide users to configure custom path in Roo settings Fixes #5866
1 parent 38d8edf commit 83486e1

File tree

1 file changed

+68
-17
lines changed
  • src/integrations/claude-code

1 file changed

+68
-17
lines changed

src/integrations/claude-code/run.ts

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ export async function* runClaudeCode(
4848
})
4949

5050
process.on("error", (err) => {
51-
processState.error = err
51+
// Enhance ENOENT errors with helpful installation guidance
52+
if (err.message.includes("ENOENT") || (err as any).code === "ENOENT") {
53+
processState.error = createClaudeCodeNotFoundError(claudePath, err)
54+
} else {
55+
processState.error = err
56+
}
5257
})
5358

5459
for await (const line of rl) {
@@ -76,6 +81,12 @@ export async function* runClaudeCode(
7681
const { exitCode } = await process
7782
if (exitCode !== null && exitCode !== 0) {
7883
const errorOutput = processState.error?.message || processState.stderrLogs?.trim()
84+
85+
// If we have a specific ENOENT error, throw that instead
86+
if (processState.error && processState.error.name === "ClaudeCodeNotFoundError") {
87+
throw processState.error
88+
}
89+
7990
throw new Error(
8091
`Claude Code process exited with code ${exitCode}.${errorOutput ? ` Error output: ${errorOutput}` : ""}`,
8192
)
@@ -144,22 +155,31 @@ function runProcess({
144155
args.push("--model", modelId)
145156
}
146157

147-
const child = execa(claudePath, args, {
148-
stdin: "pipe",
149-
stdout: "pipe",
150-
stderr: "pipe",
151-
env: {
152-
...process.env,
153-
// Use the configured value, or the environment variable, or default to CLAUDE_CODE_DEFAULT_MAX_OUTPUT_TOKENS
154-
CLAUDE_CODE_MAX_OUTPUT_TOKENS:
155-
maxOutputTokens?.toString() ||
156-
process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS ||
157-
CLAUDE_CODE_DEFAULT_MAX_OUTPUT_TOKENS.toString(),
158-
},
159-
cwd,
160-
maxBuffer: 1024 * 1024 * 1000,
161-
timeout: CLAUDE_CODE_TIMEOUT,
162-
})
158+
let child
159+
try {
160+
child = execa(claudePath, args, {
161+
stdin: "pipe",
162+
stdout: "pipe",
163+
stderr: "pipe",
164+
env: {
165+
...process.env,
166+
// Use the configured value, or the environment variable, or default to CLAUDE_CODE_DEFAULT_MAX_OUTPUT_TOKENS
167+
CLAUDE_CODE_MAX_OUTPUT_TOKENS:
168+
maxOutputTokens?.toString() ||
169+
process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS ||
170+
CLAUDE_CODE_DEFAULT_MAX_OUTPUT_TOKENS.toString(),
171+
},
172+
cwd,
173+
maxBuffer: 1024 * 1024 * 1000,
174+
timeout: CLAUDE_CODE_TIMEOUT,
175+
})
176+
} catch (error: any) {
177+
// Handle ENOENT errors immediately when spawning the process
178+
if (error.code === "ENOENT" || error.message?.includes("ENOENT")) {
179+
throw createClaudeCodeNotFoundError(claudePath, error)
180+
}
181+
throw error
182+
}
163183

164184
// Prepare stdin data: Windows gets both system prompt & messages (avoids 8191 char limit),
165185
// other platforms get messages only (avoids Linux E2BIG error from ~128KiB execve limit)
@@ -223,3 +243,34 @@ function attemptParseChunk(data: string): ClaudeCodeMessage | null {
223243
return null
224244
}
225245
}
246+
247+
/**
248+
* Creates a user-friendly error message for Claude Code ENOENT errors
249+
*/
250+
function createClaudeCodeNotFoundError(claudePath: string, originalError: Error): Error {
251+
const platform = os.platform()
252+
253+
let suggestion: string
254+
switch (platform) {
255+
case "darwin": // macOS
256+
case "win32": // Windows
257+
case "linux":
258+
default:
259+
suggestion = "Please install Claude Code CLI:\n" +
260+
"1. Visit https://claude.ai/download to download Claude Code\n" +
261+
"2. Follow the installation instructions for your operating system\n" +
262+
"3. Ensure the 'claude' command is available in your PATH\n" +
263+
"4. Alternatively, configure a custom path in Roo settings under 'Claude Code Path'"
264+
break
265+
}
266+
267+
const errorMessage = `Claude Code executable '${claudePath}' not found.
268+
269+
${suggestion}
270+
271+
Original error: ${originalError.message}`
272+
273+
const error = new Error(errorMessage)
274+
error.name = "ClaudeCodeNotFoundError"
275+
return error
276+
}

0 commit comments

Comments
 (0)