claude-code - 💡(How to fix) Fix settings.json: top-level `hooks` key dropped when granting a permission

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

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.

Root Cause

Likely root cause

Fix Action

Fix / Workaround

  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 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.

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.

Code Example

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

---

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

---

> Run `echo hello`

---

jq '.hooks // "missing"' ~/.claude/settings.json
   # "missing"
RAW_BUFFERClick to expand / collapse

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:

    {
      "permissions": { "allow": [] },
      "model": "opus",
      "hooks": {
        "PreToolUse": [
          { "hooks": [{ "type": "command", "command": "my-tool hook PreToolUse" }] }
        ]
      }
    }
  2. Confirm the file has hooks:

    jq '.hooks | keys' ~/.claude/settings.json
    # ["PreToolUse"]
  3. Start a Claude Code session and run a command requiring a permission you haven't granted before:

    > Run `echo hello`

    Accept the permission prompt.

  4. Re-check the file:

    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.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix settings.json: top-level `hooks` key dropped when granting a permission