settings.json: top-level `hooks` key dropped when granting a permission
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:
- Write a
~/.claude/settings.jsoncontaining ahookskey:
``json``
{
"permissions": { "allow": [] },
"model": "opus",
"hooks": {
"PreToolUse": [
{ "hooks": [{ "type": "command", "command": "my-tool hook PreToolUse" }] }
]
}
}
- Confirm the file has
hooks:
``bash``
jq '.hooks | keys' ~/.claude/settings.json
# ["PreToolUse"]
- Start a Claude Code session and run a command requiring a permission you haven't granted before:
``echo hello
> Run ``
Accept the permission prompt.
- 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:
- Tolerant round-trip: parse into an object that preserves unknown top-level keys, mutate only the targeted path, serialize back.
- JSON Patch: apply an RFC 6902
addoperation 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):
- Tokenwise (this repo): https://github.com/ajvikram/Tokenwise
- Likely any community hook installer
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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗