One schema-invalid hook matcher silently disables ALL settings.json hooks (no error)
Summary
A single schema-invalid matcher in a hooks entry of settings.json causes Claude Code to **silently discard the entire hooks configuration — every hook across every event (PreToolUse, PostToolUse, UserPromptSubmit, Stop, SessionStart, …) stops firing — with no error, warning, or diagnostic anywhere** (/doctor, startup, or verbose output). A recent CLI update appears to have tightened matcher validation such that one bad entry now nukes the whole config rather than being ignored.
For us this produced a ~30-hour silent outage: ~109 configured hooks (telemetry, guardrails, enforcement) all inert, undetected, because the failure mode also disables any hook you might use to detect it.
Environment
- Claude Code 2.1.202 (also observed on 2.1.197–2.1.198). The tolerance change landed via auto-update around this range.
- macOS (Darwin 25.x), zsh. Entrypoints observed:
cliandclaude-desktop.
Reproduction (minimal, deterministic)
- Start from a
~/.claude/settings.jsonwith any working hook whose command writes a marker, e.g. aStophook that appends a line to a file. - Add one hook group with a schema-invalid
matcher. Either form triggers it:
"matcher": {"type": "always"}— an object where a string is expected, or"matcher": "*"— a bare*(not a valid regex).
- Start an interactive session and complete a turn.
- Observed: the marker file is never written — and neither are any of the other, still-valid hooks. The one invalid entry silently disables the whole
hooksblock. No error is shown at startup or in/doctor. - Remove the invalid entry → all hooks fire again. (We confirmed this with a same-version A/B: clean config → hooks fire; add the invalid matcher → all hooks dead; remove it → restored.)
Expected
One of:
- Reject only the invalid entry (fail-open for the rest of the hooks), or
- Surface a loud validation error at startup / in
/doctornaming the offending event + index, and refuse to run rather than silently no-op.
Either would have turned a 30-hour invisible outage into a one-line diagnostic.
Actual
The entire hooks config is silently ignored. No error, no warning, no indication in /doctor. Behavior is indistinguishable from "no hooks configured," so nothing signals that a large configured surface has gone dark.
Impact
Any user with a non-trivial hooks setup is one typo (object-vs-string matcher, stray *) away from silently losing all hook-based observability and guardrails, with no signal. The blast radius is total (all events, all hooks) and the detection surface is the same thing that's disabled.
Suggested fixes (in preference order)
- Validate
matcherper-entry and skip only the invalid entry, keeping the rest live. - If strict-fail is preferred, emit a clear error (event + index + reason) at startup and in
/doctor— never a silent drop. - Add a
claude doctor/--validate-settingscheck that flags non-string / non-regex matchers.
Secondary observation (lower confidence — may be related or separate)
Before we found the matcher bug, we had recorded that PostToolUse hooks matching MCP tool calls (mcp__<server>__<tool>) were not firing even when built-in tools triggered hooks. That observation predates the matcher issue. Now that we know a single invalid entry can silently disable the whole hooks block, we can't rule out that the earlier symptom shared this root cause, so we're flagging it as needs re-verification on a known-clean config rather than asserting it as an independent bug. Happy to file separately with a fresh repro if it reproduces post-fix.