[BUG] `bin/claude.exe` pre-postinstall stub has no shebang — surfaces as cryptic `spawn ENOEXEC` to SDK consumers
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
The bin/claude.exe file shipped in the @anthropic-ai/claude-code npm tarball — the pre-postinstall stub that's supposed to print a helpful "claude native binary not installed" error when the package's postinstall (install.cjs) didn't run — has no shebang line.
When a downstream SDK consumer (e.g. @anthropic-ai/claude-agent-sdk given a pathToClaudeCodeExecutable) tries to spawn() it, the kernel returns ENOEXEC before the script ever runs. The consumer sees spawn ENOEXEC instead of the stub's own friendly message — which is undebuggable without reading the consuming SDK's source.
We hit this in production for an SDK consumer whose corporate npm script firewall blocked the postinstall lifecycle hook. The stub was correctly left on disk, but its useful diagnostic never reached the user.
What Should Happen?
The stub should execute and print its own diagnostic message, so the user sees actionable text instead of spawn ENOEXEC. A one-line #!/bin/sh at the top of the stub (plus exec bit set in the tarball) fixes this.
Error Messages/Logs
Error: spawn ENOEXEC
errno: -8
code: 'ENOEXEC'
syscall: 'spawn'
path: '.../node_modules/@anthropic-ai/claude-code/bin/claude.exe'
Inspecting the stub directly:
$ file package/bin/claude.exe
package/bin/claude.exe: ASCII text
$ ls -la package/bin/claude.exe
-rw-r--r-- 1 user staff 500 Oct 26 1985 package/bin/claude.exe
$ head -1 package/bin/claude.exe
echo "Error: claude native binary not installed." >&2
$ od -c package/bin/claude.exe | head -1
0000000 e c h o " E r r o r : c l a
First two bytes are `ec` (the `e` of `echo`), not `#!` — so `execve` rejects it on macOS and Linux.
Steps to Reproduce
- Download a clean tarball without running scripts:
``sh``
npm pack @anthropic-ai/claude-code --ignore-scripts
tar -xzf anthropic-ai-claude-code-2.1.148.tgz
- Inspect
package/bin/claude.exe— noteASCII text, 500 bytes, no shebang, mode0644(no exec bit). - Attempt to spawn it the same way
@anthropic-ai/claude-agent-sdkdoes when given apathToClaudeCodeExecutablewithout a.jsextension:
``js``
require('child_process').spawn('./package/bin/claude.exe', ['--version']);
// Error: spawn ENOEXEC
- The stub's
echolines never run, even though the file's content is a valid shell script.
This exact failure path occurs in the wild whenever the package's postinstall didn't run. Common triggers:
- npm/yarn/pnpm script firewalls or supply-chain proxies (Socket, Snyk, Phylum, internal tooling) that block
postinstalllifecycle hooks npm install --ignore-scriptsset in user.npmrcor CI- Locked-down corporate egress that blocks the postinstall's binary download
- pnpm strict-mode configurations
node_modulesdirectories copied across platforms (Docker layer cache, shared volumes)
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.148 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Prepend a shebang to the stub and ship it executable:
#!/bin/sh
echo "Error: claude native binary not installed." >&2
echo "" >&2
echo "Either postinstall did not run (--ignore-scripts, some pnpm configs)" >&2
echo "or the platform-native optional dependency was n..." >&2
exit 1
chmod +x bin/claude.exe # in whatever script builds/publishes the tarball
After that change, the same downstream spawn() would surface the stub's own diagnostic and exit non-zero, instead of failing with ENOEXEC. Consumers can still detect the failure programmatically, but users immediately see what's wrong and what to do.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗