[BUG] A hooks-shaped section under another top-level settings key silently stops every hook in the file

Status Open
Maintainer reply None cached
Activity 0 comments · opened Oct 1, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [ ] I am using the latest version of Claude Code: not confirmed; the version is below

What's Wrong?

In the desktop app, a project's .claude/settings.json that holds a valid hooks section and another top-level key holding a hooks-shaped object makes the session run no hook from that file, the valid ones under hooks included. Examples of such a key: "disabledHooks": { "PreToolUse": [ ... ] }, or "_hooks": { "PreToolUse": [ ... ] }.

Nothing tells anyone. The user saw no settings error notice in the app, and nothing reached the agent. The documentation says a schema error in a project settings file brings "a settings error notice".

A natural way to meet it is parking hooks under another key to switch them off for a while. Every hook in the file then stops, guards included, silently.

Measured the same way, these were ignored, with the file's hooks still running, so only a hooks-shaped section under a key other than hooks had the effect:

  • top-level keys holding strings: "_sample": "...", "_hooks": "..."
  • top-level keys holding a plain object: "probeNote": { ... }, "_hooks": { "probe": 1 }
  • an unknown event name under hooks: "_PreToolUse": [ ... ]
  • an extra key inside a matcher entry, or inside a hook object

What Should Happen?

Either the extra key is ignored like the other unknown keys, or the file is rejected with the settings error notice the documentation describes, so the user knows their hooks are off.

Error Messages/Logs

None, which is the problem: no notice to the user and no message to the agent.

Steps to Reproduce

  1. A project whose .claude/settings.json has a working PreToolUse hook, such as one that refuses a command or appends a line to a log file.
  2. Mid-session, add a second top-level key to that file:
"disabledHooks": {
  "PreToolUse": [
    { "matcher": "Read", "hooks": [ { "type": "command", "command": "node", "args": ["probe.mjs", "parked"] } ] }
  ]
}
  1. Make any tool call. The working hook no longer runs. Remove the key, and it runs again from the next call.

How it was told apart from a file not yet reloaded: each round held one control hook that had to fire on Read, and a guard hook that refuses a particular shell command. A file not yet reloaded keeps the old hooks, so the guard still refuses. A file reloaded with no hook running lets the command through.

Claude Model

claude-opus-5-5

Is this a regression?

I don't know.

Claude Code Version

Claude desktop app 2.16120.0, the Code tab; Agent SDK 0.3.284; entrypoint claude-desktop. Not tested in the CLI.

Platform

Anthropic API

Operating System

Windows 11 Pro 10.0.26200

Found and written up by Claude Code, in a session that ran the test with its user, 2026-10-01.

View original on GitHub ↗