PreToolUse hooks are not invoked for schema-invalid edits to .claude/settings.json — security hooks get no observability of settings-tamper attempts
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?
The hooks documentation says PreToolUse fires "before a tool call executes" with no documented exceptions. Empirically, when the agent uses the Edit tool on a .claude/settings.json file and the resulting content fails settings-schema validation, Claude Code rejects the edit itself ("Claude Code settings.json validation failed after edit: ...") without ever invoking PreToolUse hooks — our hook process is not spawned and writes no trace entry. Schema-valid edits to the same file DO invoke PreToolUse normally.
The result: hook-based security/audit tooling cannot observe schema-invalid tamper attempts against the settings file. The attempt leaves no hook-visible record at all, and the "validation failed after edit" wording suggests the edit is applied before validation, i.e. the tool call executed without its PreToolUse gate.
Reproduction
Setup: register a PreToolUse command hook (matcher Edit|Write) that denies edits matching **/.claude/settings.json and **/.env, and logs every invocation to a trace file.
- Control (hook works): ask the agent to edit a scratch
.env→ hook fires, denies, trace entry written. ✅ - Schema-INVALID settings edit: ask the agent to Edit
.claude/settings.jsonadding an unrecognized top-level key (e.g."probeBaseline": true) → Edit tool returns "Claude Code settings.json validation failed after edit: ... Unrecognized field: probeBaseline". The hook is never invoked (no process spawn, no trace entry). File ends up unchanged. ❌ hook bypassed - Schema-VALID settings edit: ask the agent to Edit the same file removing an entry from
permissions.deny(schema-valid) → hook fires and denies as expected, trace entry written. ✅
Observed identically with forward-slash paths on Linux (not a Windows path-separator issue). Steps 2/3 run headless (claude -p ... --permission-mode acceptEdits) with a should-pass control edit in the same session to rule out permission-layer false positives. (Note re: the closed headless-hooks issue #36071: in these headless probes hooks fired and blocked correctly, so this is not a headless artifact.)
Not a duplicate of the environment-wide hook failures (#53254, #26962, #24346 — all closed): in our repro, hooks fire and deny correctly in the same session (step 1 control); only Edit calls on .claude/settings.json whose result fails schema validation skip the hook (step 2), while schema-valid edits to the same file invoke it (step 3). Related but distinct: #44482 (closed — pre-approved permission bypass of hooks, a different trigger), #30519 (permissions-matching umbrella; relevant only to the Write() rule footnote below).
Expected
Either (a) PreToolUse fires for every Edit/Write tool call including those targeting settings files, with schema validation happening after the hook gate; or (b) the special-casing is documented in the hooks docs so security tooling authors know settings-file edits can bypass PreToolUse observation when schema-invalid.
Impact
- Hook-based governance/audit tools (ours governs tool calls against a local policy) have a blind spot: settings-tamper attempts that happen to be schema-invalid are invisible to them.
- The error text implies apply-then-validate ordering for a security-sensitive file.
What works (for anyone else building on this)
permissions.deny rules ARE evaluated for settings self-edits, before hooks, and block them — including the edit that would remove the deny rules themselves (verified on 2.1.210: "deny": ["Edit(**/.claude/settings.json)"] blocks Edit and Write tools; note the session-start warning is correct that Write(path) rules are never matched by file permission checks, Edit rules cover all file-editing tools). So native deny rules are a viable mitigation; the remaining issue is hook observability of the schema-invalid class.
Questions
- Is the pre-hook rejection of schema-invalid settings edits intentional?
- Is the ordering (edit applied → validated → reverted?) what the "validation failed after edit" message implies, and is the file ever transiently modified on disk?
- Could schema-invalid settings edits emit PreToolUse (or at least PostToolUseFailure-style) events so audit hooks see the attempt?
Environment Info
- Platform: linux (WSL2, Ubuntu) — also observed on Windows 11 desktop app, 2026-07-11
- Version: 2.1.210
- Hook: PreToolUse
commandhook matchingEdit|Write|...(AstraSync Local Guard, registered in~/.claude/settings.json)
Errors
[]This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗