Stop hook fails with 'spawn /bin/sh ENOENT' on every session end (v2.1.39)
Bug Description
The Stop hook consistently fails with the following error on every session end:
Stop hook error: Failed with non-blocking status code: Error occurred while executing hook command: spawn /bin/sh ENOENT
Environment
- Claude Code version: 2.1.39
- OS: Ubuntu 22.04 (Linux)
- Shell: /bin/sh → dash (verified to exist and work)
Hook Configuration
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python3 /home/user/.nathan/scripts/stop.py",
"timeout": 10
}
]
}
]
What I've Verified
/bin/shexists and works:ls -la /bin/sh→lrwxrwxrwx /bin/sh -> dash/bin/sh -c "echo test"works correctly from a terminal- The hook script itself runs correctly when invoked directly:
echo '{}' | python3 stop.pyexits 0 - All other hooks (UserPromptSubmit, PreToolUse, PostToolUse, PreCompact) work correctly
- The error only occurs on the Stop hook
Root Cause Hypothesis
Node.js spawn('/bin/sh', ['-c', 'command']) fails with ENOENT when the Stop hook fires during Claude Code process teardown. It appears that by the time the Stop hook runs, the Node.js runtime's ability to spawn child processes is already partially torn down — even though /bin/sh exists on the filesystem and works in normal execution.
This is a known pattern: shutdown hooks in Node.js sometimes can't access system resources that were available during normal operation.
Impact
- The hook is marked "non-blocking" so Claude Code continues normally
- However, session finalization actions (writing daily logs, merging state) do not run
- This affects every single session end — it is 100% reproducible
Workaround
None found. The only viable workaround is to move stop-hook logic to PreCompact instead, which runs reliably.
Request
Please ensure the Stop hook's command is spawned before the Node.js process begins teardown, or document that Stop hooks cannot reliably spawn child processes.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗