Summary
On Windows, --backend claude-cli fails for every chunk with [WinError 2] The system cannot find the file specified, because _call_claude_cli in graphify/llm.py invokes subprocess.run(["claude", ...]) and Windows' CreateProcess cannot directly execute the claude.ps1 script that npm installs. The bare-name claude resolves via PATHEXT — and on systems where .PS1 is listed in PATHEXT ahead of .CMD, Python subprocess.run resolves to the PowerShell script and crashes. claude.cmd (also installed by npm) IS directly executable by CreateProcess and works fine.
Result: --backend claude-cli is broken on Windows for a non-trivial fraction of installs. All N semantic chunks fail with the same WinError 2, then graphify aborts with all semantic chunks failed for backend 'claude-cli'.
Environment
- OS: Windows 11 Home 10.0.26200
- Python: 3.12.12 (uv-bundled)
- Graphify: v0.8.24 (installed via
uv tool install git+https://github.com/safishamsi/graphify)
- Claude Code: v2.1.142
- npm
claude install layout (typical):
C:\Users\<user>\AppData\Roaming\npm\claude (shell shim, not Windows-executable)
C:\Users\<user>\AppData\Roaming\npm\claude.cmd (Windows batch wrapper, subprocess-executable)
C:\Users\<user>\AppData\Roaming\npm\claude.ps1 (PowerShell script, NOT subprocess-executable)
shutil.which("claude") returns claude.ps1 on this machine (PATHEXT prefers .PS1).
Reproduction
# Prereqs: claude CLI installed + authenticated; graphify installed; ANTHROPIC_API_KEY NOT needed for claude-cli backend
$env:GRAPHIFY_CLAUDE_CLI_MODEL = "claude-sonnet-4-6"
mkdir _gtest
copy README.md _gtest\
graphify extract _gtest --backend claude-cli --max-concurrency 1
Actual output
[graphify extract] scanning C:\...\_gtest
[graphify extract] found 0 code, 1 docs, 0 papers, 0 images
[graphify extract] semantic extraction on 1 files via claude-cli...
[graphify] chunk 1/1 failed: [WinError 2] The system cannot find the file specified
[graphify] WARNING: 1/1 semantic chunk(s) failed — see errors above. Partial results returned.
[graphify extract] error: all semantic chunks failed for backend 'claude-cli' (1 uncached files) - see per-chunk errors above. If you see 'requires the X package', run `pip install X` and retry.
Expected output
Same successful extraction the macOS/Linux paths produce — chunk 1/1 done, graph artifacts written to _gtest/graphify-out/.
Root cause
graphify/llm.py::_call_claude_cli (around line 510):
cli_args = [
"claude", "-p",
"--output-format", "json",
"--no-session-persistence",
"--system-prompt", _extraction_system(deep=deep_mode),
]
# ...
proc = subprocess.run(cli_args, ..., capture_output=True, ...)
subprocess.run(["claude", ...]) on Windows hands the list to CreateProcess, which:
- Resolves
claude via PATH + PATHEXT
- On many Windows configs, PATHEXT lists
.PS1 (added by PowerShell Core / Claude installer / user customization), causing resolution to claude.ps1
CreateProcess cannot execute .ps1 files directly — needs explicit powershell.exe -File
- Returns
ERROR_FILE_NOT_FOUND → Python raises FileNotFoundError([WinError 2])
When PATHEXT happens to put .CMD ahead of .PS1 (or .PS1 is absent), claude.cmd resolves first and the path works. The bug is conditional on PATHEXT ordering.
Proposed fix
Prefer the claude.cmd Windows batch wrapper explicitly on Windows. It's installed alongside claude.ps1 by npm and is CreateProcess-executable. Minimal patch in _call_claude_cli:
def _call_claude_cli(user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False) -> dict:
+ import platform
import shutil
import subprocess
if shutil.which("claude") is None:
raise RuntimeError(
"Claude Code CLI not found on $PATH. Install from "
"https://claude.ai/code and run `claude` once to authenticate."
)
+ # Windows: subprocess.run cannot directly execute claude.ps1. Prefer
+ # claude.cmd (npm also installs it) which CreateProcess handles fine.
+ if platform.system() == "Windows":
+ claude_exec = shutil.which("claude.cmd") or shutil.which("claude")
+ else:
+ claude_exec = "claude"
cli_args = [
- "claude", "-p",
+ claude_exec, "-p",
"--output-format", "json",
"--no-session-persistence",
"--system-prompt", _extraction_system(deep=deep_mode),
]
Verified locally: applying the above to a v0.8.24 install lets a 185KB single-file extract succeed via --backend claude-cli (158 nodes, 142 edges, 37 communities, $0 incremental cost via subscription).
Workaround (without the patch)
Set PATHEXT in the shell that invokes graphify so .CMD resolves before .PS1:
$env:PATHEXT = ".CMD;.EXE;.BAT;.PS1;" + $env:PATHEXT
graphify extract ... --backend claude-cli
This is fragile (depends on user shell config and is non-obvious) — the in-code fix is the proper solution.
Cross-references
Happy to open a PR if useful. Reproduces 100% on Windows installs where PATHEXT lists .PS1 before .CMD.
Summary
On Windows,
--backend claude-clifails for every chunk with[WinError 2] The system cannot find the file specified, because_call_claude_cliingraphify/llm.pyinvokessubprocess.run(["claude", ...])and Windows'CreateProcesscannot directly execute theclaude.ps1script that npm installs. The bare-nameclauderesolves via PATHEXT — and on systems where.PS1is listed in PATHEXT ahead of.CMD, Pythonsubprocess.runresolves to the PowerShell script and crashes.claude.cmd(also installed by npm) IS directly executable byCreateProcessand works fine.Result:
--backend claude-cliis broken on Windows for a non-trivial fraction of installs. All N semantic chunks fail with the sameWinError 2, thengraphifyaborts withall semantic chunks failed for backend 'claude-cli'.Environment
uv tool install git+https://github.com/safishamsi/graphify)claudeinstall layout (typical):C:\Users\<user>\AppData\Roaming\npm\claude(shell shim, not Windows-executable)C:\Users\<user>\AppData\Roaming\npm\claude.cmd(Windows batch wrapper, subprocess-executable)C:\Users\<user>\AppData\Roaming\npm\claude.ps1(PowerShell script, NOT subprocess-executable)shutil.which("claude")returnsclaude.ps1on this machine (PATHEXT prefers.PS1).Reproduction
Actual output
Expected output
Same successful extraction the macOS/Linux paths produce —
chunk 1/1 done, graph artifacts written to_gtest/graphify-out/.Root cause
graphify/llm.py::_call_claude_cli(around line 510):subprocess.run(["claude", ...])on Windows hands the list toCreateProcess, which:claudevia PATH + PATHEXT.PS1(added by PowerShell Core / Claude installer / user customization), causing resolution toclaude.ps1CreateProcesscannot execute.ps1files directly — needs explicitpowershell.exe -FileERROR_FILE_NOT_FOUND→ Python raisesFileNotFoundError([WinError 2])When PATHEXT happens to put
.CMDahead of.PS1(or.PS1is absent),claude.cmdresolves first and the path works. The bug is conditional on PATHEXT ordering.Proposed fix
Prefer the
claude.cmdWindows batch wrapper explicitly on Windows. It's installed alongsideclaude.ps1by npm and isCreateProcess-executable. Minimal patch in_call_claude_cli:Verified locally: applying the above to a v0.8.24 install lets a 185KB single-file extract succeed via
--backend claude-cli(158 nodes, 142 edges, 37 communities, $0 incremental cost via subscription).Workaround (without the patch)
Set PATHEXT in the shell that invokes graphify so
.CMDresolves before.PS1:This is fragile (depends on user shell config and is non-obvious) — the in-code fix is the proper solution.
Cross-references
_call_claude_cliimplementation:graphify/llm.py:486--backend claude-cli(route through Claude Code CLI, noANTHROPIC_API_KEYneeded) #855 (original claude-cli backend feature request — closed/shipped), How properly setup graphify to work with claude code cli? #749 (claude-cli setup help — possibly affected by this bug on Windows users)claudeCLI npm install layout: ships.ps1+.cmd+ bare-name variantsHappy to open a PR if useful. Reproduces 100% on Windows installs where PATHEXT lists
.PS1before.CMD.