settings.json: top-level `hooks` key dropped when granting a permission

Resolved 💬 3 comments Opened May 17, 2026 by ajvikram Closed Jun 18, 2026

Claude Code drops top-level hooks from ~/.claude/settings.json when granting a new permission

Summary

When the user accepts a new permission during a Claude Code session, the CLI rewrites ~/.claude/settings.json but silently drops the top-level hooks key if one is present. Other top-level keys (permissions, model, etc.) are preserved; only hooks is lost.

This breaks any external tool that registers PreToolUse / PostToolUse / Stop hooks via settings.json, including hook-based observability tools and policy enforcers.

Reproduction

Minimal repro with a verifier script:
https://github.com/ajvikram/Tokenwise/tree/main/docs/issues/claude-code-hooks-wipe

Inline steps:

  1. Write a ~/.claude/settings.json containing a hooks key:

``json
{
"permissions": { "allow": [] },
"model": "opus",
"hooks": {
"PreToolUse": [
{ "hooks": [{ "type": "command", "command": "my-tool hook PreToolUse" }] }
]
}
}
``

  1. Confirm the file has hooks:

``bash
jq '.hooks | keys' ~/.claude/settings.json
# ["PreToolUse"]
``

  1. Start a Claude Code session and run a command requiring a permission you haven't granted before:

``
> Run
echo hello
``

Accept the permission prompt.

  1. Re-check the file:

``bash
jq '.hooks // "missing"' ~/.claude/settings.json
# "missing"
``

permissions.allow correctly contains the new Bash(echo hello) entry. hooks is gone.

Expected behaviour

After accepting a permission, the CLI should preserve all top-level keys it does not own. Only permissions.allow should be modified.

Likely root cause

The permission-grant rewrite appears to deserialize into a known/typed shape and re-serialize, which strips unknown keys. Two clean fixes:

  1. Tolerant round-trip: parse into an object that preserves unknown top-level keys, mutate only the targeted path, serialize back.
  2. JSON Patch: apply an RFC 6902 add operation against /permissions/allow/- directly, leaving the rest of the document untouched.

The second is the safer fix because it cannot regress on any other top-level key (current or future).

Impact

Any tool that integrates with Claude Code via settings.json hooks is unreliable across an active session if the user grants new permissions. Tools currently affected (non-exhaustive):

The user-facing symptom is that hook integrations "stop working" mid-session for no apparent reason. Today this requires shell-prompt workarounds — see Tokenwise's heal hook for one example. These workarounds are reactive and racy; they cannot guarantee correctness.

Environment

  • Claude Code version: <run claude --version>
  • OS: macOS 14 (also reproducible on Linux per a quick sanity check)
  • Shell: zsh (irrelevant — the write happens inside the CLI)

What would help

Even just confirmation that this is on Anthropic's radar, with a target version where it'll be fixed, would let downstream tool authors decide whether to invest in heavier workarounds or simply wait.

Happy to test a fix against the repro above.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗