Free troubleshooting guide
Claude Code hooks not firing?
First separate “the command never ran” from “the command ran but allowed the action.” Then check one layer at a time.
Updated October 19, 2026 · Examples: Node 20+, macOS/Linux. Windows untested.
Start the six-step checklistDiagnose the symptom first
- Missing from
/hooks: start with settings location and valid JSON. - Listed, but never runs: check the event and case-sensitive tool matcher.
- Runs with an error: check the command path, runtime and input.
- Runs but does not block: check the event, exit status and the paths the script actually handles.
A passing standalone script test does not prove that Claude Code loaded the hook. A hook listed in the menu does not prove that its logic is correct. Verify both separately.
Six checks, in order
1. Confirm which settings file you changed
Project hooks belong in .claude/settings.json in the project root; user-level hooks use ~/.claude/settings.json. Open /hooks in Claude Code and look under the event you configured. Merge into the existing hooks object rather than adding a duplicate key or replacing other hooks.
From the project root, this command only parses the project settings file. It does not print settings or change them:
node -e 'JSON.parse(require("node:fs").readFileSync(".claude/settings.json", "utf8")); console.log("Valid JSON")'
JSON does not allow comments or trailing commas. Valid JSON is only a syntax check, not proof of a valid hook configuration. If changes have not appeared after a few seconds, restart the session and check again. If /hooks reports that only managed hooks are allowed, respect that policy; a project file cannot enable this sample there.
2. Match the tool that actually performs the action
Edit and Write are not Bash. A matcher for edit-style tools will not intercept a shell command that writes a file. Matchers are case-sensitive: edit is not Edit. Inspect the tool name, not just the natural-language request.
3. Use the right event for the outcome
PreToolUse runs before the tool. The free protect-files example uses this event to reject selected edit paths. PostToolUse runs afterward; it cannot undo a file change that already happened. Do not test a supposed blocker against real credentials.
4. Make the command work from the project directory
Check node --version in the environment that launches Claude Code. Keep protect-files.mjs and its imported lib.mjs together. A missing helper is a runtime error, not a successful block. The sample uses a quoted project-root path so spaces and subdirectories do not break script lookup:
node "$CLAUDE_PROJECT_DIR/.claude/hooks/protect-files.mjs"
This is the command inside the hook configuration, where Claude Code supplies CLAUDE_PROJECT_DIR. It may be unset in an ordinary terminal. For a standalone test, use the downloaded sample's directory and node verify.mjs instead. Because Node opens the script here, adding an executable bit to the JavaScript file is not the fix for a missing Node runtime.
5. Give the hook the input it expects
This sample reads a JSON object from stdin and checks tool_input.file_path or tool_input.notebook_path. Running it with no payload does not test a protected path. Its parser treats malformed or empty input as an empty object, which can allow the action; it is not a fail-closed security boundary.
From the unpacked sample folder, this fixture tests a fictional path string. The script does not read or modify the named file:
printf '%s\n' '{"tool_name":"Edit","tool_input":{"file_path":".env.local"}}' | node protect-files.mjs
printf 'Exit status: %s\n' "$?"
Expected: a Blocked by protect-files hook message on stderr and exit status 2. Changing the fixture path to .env.example should return 0. Those are intentional placeholder-file and secret-file rules, not inconsistent behavior.
6. Verify logic, then runtime wiring
Run node verify.mjs from the sample directory. Expect seven PASS lines and 7 checks passed: five blocked paths and two allowed paths. These offline tests make no network requests and never create or edit secret files.
After merging the example configuration, confirm it appears in /hooks. Test live wiring only in a disposable project with dummy values, checking the actual tool and block result. Keep normal permission controls enabled. A successful hook may be silent; no message alone is not evidence that it did not run.
Try the exact example used in this guide
No signup, purchase or email required. The ZIP contains two hook files, example settings, a seven-check offline verifier, README and license. Unzip into a new empty folder and inspect the source before running it.
Download the free debugging sampleRead the sample source and setup instructions. Preserve existing settings and hooks when merging; remove only this hook's command entry if you stop using it.
Scope: selected edit paths only. It does not block reads or Bash writes, and does not enforce Next.js server/client boundaries. Keep review, permissions and CI.
If the same workflow mistakes keep recurring
One hook is a starting point, not a full workflow
The existing $15 Next.js Claude Code Workflow Pack adds five hooks in total, a CLAUDE.md template, three subagents, a preflight command, installer and 52 self-tests. It is a static, self-applied download, not managed setup or a security guarantee.
Compare the free sample and full packOne-time purchase via Gumroad. Sold as-is; no updates, support or custom setup included.
Still defining project rules? Start with the free CLAUDE.md template for Next.js.
Sources and limits
Configuration and troubleshooting reference: Anthropic's hook guide (checked October 19, 2026). Runtime behavior can vary by version and policy. The offline outcomes above are verified against the exact free sample linked here, not every hook or Claude Code version.