[BUG] `bin/claude.exe` pre-postinstall stub has no shebang — surfaces as cryptic `spawn ENOEXEC` to SDK consumers

Resolved 💬 2 comments Opened May 22, 2026 by johnkerl Closed Jun 26, 2026

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

  1. 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
``

  1. Inspect package/bin/claude.exe — note ASCII text, 500 bytes, no shebang, mode 0644 (no exec bit).
  2. Attempt to spawn it the same way @anthropic-ai/claude-agent-sdk does when given a pathToClaudeCodeExecutable without a .js extension:

``js
require('child_process').spawn('./package/bin/claude.exe', ['--version']);
// Error: spawn ENOEXEC
``

  1. The stub's echo lines 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 postinstall lifecycle hooks
  • npm install --ignore-scripts set in user .npmrc or CI
  • Locked-down corporate egress that blocks the postinstall's binary download
  • pnpm strict-mode configurations
  • node_modules directories 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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗