[BUG] ConfigChange hook emitting blocking decision is not respected by claude code CLI

Status Fixed / completed
Reported on v2.1.117
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Jul 20, 2026 · closed Aug 17, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

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)
  • [ ] I am using the latest version of Claude Code

What's Wrong?

I've created a ConfigChange hook that checks if the settings.json files are valid JSON (common pitfall: editing the settings mid-session) and the hook is supposed to block if it isn't.

Through the hook script's logging utility I can see that it does fire and does report broken JSON. Testing the hook from the command line shows the same behavior.

Both emitting a JSON decision to STDOUT, and exiting with code 2 fails to interrupt the session in the face of a broken settings.json

The hook is part of a suite of hooks that are also used on PreToolUse and elsewhere, using the same library code, and all of those work correctly. This seems to be a clear problem in how the CLI handles the return from the ConfigChange hook.

What Should Happen?

The session should immediately interrupt upon a ConfigChange hook outputting a blocking JSON decision or exiting with code 2.

Error Messages/Logs

Steps to Reproduce

  1. Add a ConfigChange hook to the project settings .claude/settings.json with the command "exit 2" to output a blocking exit code
  2. Run a claude code session (use a dummy first prompt like 'Hi claude')
  3. Edit the .claude/settings.json in some way

and

  1. Add a ConfigChange hook to the project settings .claude/settings.json with the command "echo '{\"decision\":\"block\",\"reason\":\"none\"}'" to output a blocking decision to STDOUT
  2. Run a claude code session (use a dummy first prompt like 'Hi claude')
  3. Edit the .claude/settings.json in some way

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.117

Claude Code Version

2.1.196

Platform

Other

Operating System

Other Linux

Terminal/Shell

Other

Additional Information

This is a pretty critical security flaw.

View original on GitHub ↗

3 Comments

bcherny collaborator · 2 months ago

Could not reproduce on v2.1.233 (macOS) — the ConfigChange block is respected in both forms (exit code 2 and a JSON decision: "block").

Steps:

  1. In a fresh project, add a ConfigChange hook to .claude/settings.json whose command is exit 2 (also tried echo '{"decision":"block","reason":"none"}').
  2. Start claude --debug-file ./debug.txt and send a first prompt ("Hi claude").
  3. Edit .claude/settings.json to add a UserPromptSubmit hook that rejects every prompt (echo NEW_HOOK_APPLIED >&2; exit 2).
  4. Send another prompt.

Observed: the second prompt goes through normally — the newly added hook was not applied, i.e. the change was blocked. debug.txt shows ConfigChange:project_settings [exit 2] completed with status 2 followed by ConfigChange hook blocked change to .../.claude/settings.json. Control: with no ConfigChange hook, the same edit takes effect immediately and the next prompt is rejected with NEW_HOOK_APPLIED.

Expected: the same. The session is not interrupted, and no message is shown.

Assessment: this is working as designed — see https://code.claude.com/docs/en/hooks#configchange-decision-control. The intended model is that a blocking ConfigChange hook prevents the new settings from being applied to the running session; it does not stop or interrupt the session, and by design it surfaces no message to you or to Claude (only a debug-log line). This has been the behavior since ConfigChange hooks were introduced, including v2.1.117. Note that in your specific case (blocking on invalid JSON) a block is invisible for another reason: an unparseable settings file can't be applied either way, so blocked and not-blocked look identical. If you want the session to stop, that would be a feature request rather than a bug. If you are actually seeing new, valid settings take effect despite the block, please share which file you edit (policy_settings changes cannot be blocked) and a --debug-file log.

🤖 Generated with Claude Code

bcherny collaborator · 1 month ago

Thanks for the detailed report and repro script. I tested this on 2.1.233 (Linux): fresh config dir, a ConfigChange hook on ~/.claude/settings.json, then edited the file externally while a session was running.

What I observed: the hook fires, and both {"decision":"block"} and exit 2 are respected — the edited settings are not applied to the running session (visible with --debug: "ConfigChange hook blocked change to …"; a control run without the block shows the settings reload happening). So the blocking decision is working.

The part that differs from your expectation is what a block does: per the hooks docs, a ConfigChange block "blocks the configuration change from taking effect" — it prevents the new settings from being applied, but it does not interrupt the session, and no message is shown in the UI (only a debug-log line). That silence is intended behavior today, so I'm closing this as working-as-documented — reply if you can show the blocked settings actually taking effect in your session and we'll reopen.

I agree the zero-feedback block is easy to read as "not respected"; visible feedback on blocked config changes would be a reasonable feature request.

🤖 Generated with Claude Code

github-actions[bot] · 7 days ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.