-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Issue: Extension fails with ENAMETOOLONG error on large git diffs
Description
When attempting to generate commit messages for large git diffs, the extension fails with the error:
Error: spawn ENAMETOOLONG Claude CLI execution failed: spawn ENAMETOOLONG
This occurs because the extension passes the entire git diff as a base64-encoded command-line argument, which can exceed the OS command-line length limit (~8KB on most systems).
Steps to Reproduce
- Make changes to your repository that result in a large git diff (e.g., >50KB)
- Stage the changes with
git add - Click the sparkle icon in VS Code's Source Control panel to generate a commit message
- The extension fails with
ENAMETOOLONGerror
Environment
- OS: All platforms (Windows, Linux, macOS)
- Extension Version: 1.0.0
- VS Code Version: Any
Root Cause
The extension currently uses inline command-line arguments to pass the prompt to Claude CLI:
echo "very_long_base64_string" | base64 -d | claude --print --model sonnet --output-format jsonWhen the diff is large, the base64-encoded string exceeds the command-line argument length limit, causing the ENAMETOOLONG error.
Proposed Solution
Use a temporary file to store the prompt when it exceeds a safe length threshold:
- Check if the prompt length exceeds a threshold (e.g., 100KB)
- If yes, write the prompt to a temporary file
- Pipe the file contents to Claude CLI instead of using inline arguments
- Clean up the temporary file after execution
Example Implementation
// For large prompts, use a temp file to avoid command-line length limits
let command;
let tempFile: string | null = null;
const maxInlineLength = 100000; // Conservative limit
if (prompt.length > maxInlineLength) {
// Create temp file with the prompt
tempFile = path.join(os.tmpdir(), `claude-prompt-${Date.now()}.txt`);
fs.writeFileSync(tempFile, prompt, 'utf8');
// Read from temp file and pipe to claude
command = `cat '${tempFile}' | ${this.claudePath}`;
} else {
// For shorter prompts, use base64 encoding inline
const base64Prompt = Buffer.from(prompt, 'utf8').toString('base64');
command = `echo "${base64Prompt}" | base64 -d | ${this.claudePath}`;
}
// Add flags
command += ' --print --model sonnet --output-format json --dangerously-skip-permissions';
// ... execute command ...
// Cleanup in finally block
if (tempFile) {
fs.unlinkSync(tempFile);
}Benefits
- ✅ Works with git diffs of any size
- ✅ Backward compatible (small diffs still use inline base64)
- ✅ Cross-platform solution
- ✅ Automatic cleanup of temporary files
Additional Context
This issue affects all platforms and can occur whenever a commit includes extensive changes (e.g., adding large dependencies, generated files, or refactoring many files at once).
Quick fix to copiled .js file
1. Find the executeCommand methodLook for the section that builds the command (around line 280-290 in the .js file)
2. Replace the command building logic
Find this:
const base64Prompt = Buffer.from(prompt, 'utf8').toString('base64');
let command = `echo "${base64Prompt}" | base64 -d | ${this.claudePath}`;Replace with:
// Detect platform
const isWindows = process.platform === 'win32';
// For large prompts, use a temp file
const fs = require('fs');
const path = require('path');
const os = require('os');
let command;
let tempFile = null;
const maxInlineLength = isWindows ? 7000 : 100000;
if (prompt.length > maxInlineLength) {
// Create temp file with the prompt
tempFile = path.join(os.tmpdir(), `claude-prompt-${Date.now()}.txt`);
fs.writeFileSync(tempFile, prompt, 'utf8');
if (isWindows) {
command = `Get-Content -Path '${tempFile}' -Raw | ${this.claudePath}`;
} else {
command = `cat '${tempFile}' | ${this.claudePath}`;
}
} else {
const base64Prompt = Buffer.from(prompt, 'utf8').toString('base64');
if (isWindows) {
command = `[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${base64Prompt}')) | ${this.claudePath}`;
} else {
command = `echo "${base64Prompt}" | base64 -d | ${this.claudePath}`;
}
}3. Update execOptions
Find this:
shell: '/bin/bash',Replace with:
shell: isWindows ? 'powershell.exe' : '/bin/bash',Find this:
PATH: `/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:${process.env.PATH}:${process.env.HOME}/.nvm/versions/node/*/bin`Replace with:
PATH: isWindows ? process.env.PATH : `/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:${process.env.PATH}:${process.env.HOME}/.nvm/versions/node/*/bin`4. Add cleanup after the catch block
Find the end of the catch block:
catch (error) {
// ... error handling
throw new Error(`Claude CLI execution failed: ${error.message}`);
}Add after it:
finally {
// Clean up temp file if it was created
if (tempFile) {
try {
fs.unlinkSync(tempFile);
} catch (cleanupError) {
// Ignore cleanup errors
}
}
}5. Skip bash/zsh detection on Windows
Find (around line 93-120):
// Try using bash shellWrap the entire bash shell section with:
if (process.platform !== 'win32') {
// ... existing bash shell code
}Do the same for the zsh shell section (around line 120-145)