Hooks fail on non-FHS systems (NixOS): ENOENT posix_spawn '/bin/sh'
Summary
Hook commands fail on NixOS and other non-FHS Linux systems because the harness spawns hooks via \/bin/sh\, which does not exist on these systems.
\\\\
Stop hook error: Failed with non-blocking status code: Error occurred while
executing hook command: ENOENT: no such file or directory, posix_spawn '/bin/sh'
\\
Environment
- OS: NixOS (non-FHS — \
/bin\does not exist; \sh\lives at \/nix/store/.../bin/sh\) - Claude Code version: latest
- Hook type: Stop (reproduced; likely affects all hook types)
Root cause
Node.js's \child_process.exec()\ on POSIX systems unconditionally calls \posix_spawn('/bin/sh', ['-c', command], ...)\. This is fine on FHS-compliant systems but breaks anywhere \/bin/sh\ is absent, including NixOS, bubblewrap/container sandboxes without FHS bind mounts, and some minimal Docker base images.
Suggested fix
Option A — Respect \$SHELL\ (least invasive):
\\\js\
const shell = process.env.SHELL || '/bin/sh';
child_process.execFile(shell, ['-c', command], ...);
\\
Option B — PATH-based lookup (most portable):
\\\js\
// spawn with a bare name delegates resolution to execvp(), which searches PATH
child_process.spawn('sh', ['-c', command], { shell: false });
\\
Either option eliminates the hardcoded \/bin/sh\ assumption without requiring user configuration.
Workaround
Patch the host system to create \/bin/sh\ via a NixOS activation script. This works but forces FHS compatibility on the entire system just to accommodate this assumption in the harness.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗