[BUG] Claude Code in Action / Implementing the hook course teach us proccess.exit(2) + console.error
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?
Write now I am studying a course Claude Code in Action
Inside that course in section Implementing the hook - we have not actual information.
The course teach us to use
proccess.exit(2) + console.error
if we want to block something inside PretoolUse hook.
But right now we need to use
process.stdout + process.exit(0);
the hooks course teaches process.exit(2) + console.error to block PreToolUse, but this no longer
works in the current version — the correct approach is JSON to stdout with {"continue": false, "stopReason": "..."} +
process.exit(0)
What Should Happen?
Update the information inside the course.
- you have an antropic certification exame. If inside this exam you use also old information - it is a critical bug
Error Messages/Logs
● Found the issue. The hook at hooks/read_hook.js is correctly detecting .env reads, but it's broken in two ways:
1. process.exit(2) is the wrong signal. Exit code 2 is only meaningful for asyncRewake hooks. For a synchronous
PreToolUse hook, it doesn't block the operation — it just indicates an error, and Claude Code falls through and allows
the read.
2. console.error writes to stderr, not stdout. Claude Code reads control decisions from stdout. The block signal must
be JSON on stdout.
The fix: output {"continue": false, "stopReason": "..."} to stdout, then exit 0:
Steps to Reproduce
Add PreToolHooks
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Grep",
"hooks": [
{
"type": "command",
"command": "node ./hooks/read_hook.js"
}
]
},
Create a read.hooks.js
async function main() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
const toolArgs = JSON.parse(Buffer.concat(chunks).toString());
// readPath is the path to the file that Claude is trying to read
const readPath =
toolArgs.tool_input?.file_path || toolArgs.tool_input?.path || "";
// TODO: ensure Claude isn't trying to read the .env file
if(readPath.includes('.env')){
console.error("You cannot read the .env file")
process.exit(2);
}
}
main();
Then ask claude to read .env file
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
Code 2.1.126.
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Windows Terminal
Additional Information
_No response_
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗