[BUG] Hooks in a mid-session-created `.claude/settings.json` never arm until restart (settings watcher scans once at startup, skips a not-yet-existent `.claude/`)

Open 💬 0 comments Opened Jul 14, 2026 by willmcginnis

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?

If a Claude Code session starts in a directory that has no .claude/ directory, then creating .claude/settings.json (with hooks) later in that same session never arms those hooks — they don't take effect until the session is restarted. This is surprising because in-session edits to an already-existing settings file normally do hot-reload.

The twist: Claude Code itself creates the project .claude/ directory mid-session (as part of its .cc-writes atomic-write staging), so a directory that started without .claude/ ends up with one — but the watcher still never picks it up.

The tell: pre-create an empty .claude/ directory before starting the session, and the hooks fire immediately with no restart. So the trigger is specifically ".claude/ absent at session start."

What Should Happen?

Hooks written into .claude/settings.json during a session should take effect in that session — as in-session edits to an already-existing settings file already do — especially since Claude Code creates the project .claude/ directory itself mid-session. At minimum, a project .claude/ that Claude Code creates during the session should become watched.

Error Messages/Logs

No error is produced — the failure is silent. The hook simply never fires and nothing is logged.

Steps to Reproduce

  1. mkdir /tmp/repro && cd /tmp/repro — ensure no .claude/ exists.
  2. Start claude.
  3. In-session, create .claude/settings.json:

``json
{"hooks":{"PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"echo FIRED >> /tmp/repro/HOOKFIRED.log"}]}]}}
``

  1. Trigger a Bash tool call.
  2. Observed: /tmp/repro/HOOKFIRED.log is never created — the hook never fires.
  3. /exit, then claude --continue, and trigger a Bash call → now it fires (restart-only).

Contrast (non-triggering): pre-create an empty .claude/ before step 2 → the hook fires at step 4 with no restart.

Environment: Claude Code v2.1.208, macOS (arm64). Reproduces in both interactive and -p (headless) sessions.

Likely Root Cause

The settings-file watcher appears to scan the filesystem once at startup and never re-scan. Its per-settings-directory gate skips a directory that doesn't exist at that instant — an empty-but-present .claude/ passes and is watched, but a missing one is excluded, and nothing watches the project root for a .claude/ being created later. Meanwhile Claude Code lazily creates the project .claude/ itself (as part of its .cc-writes atomic-write staging) after that one-time scan — so the just-created .claude/, and any settings.json/hooks inside it, are never watched.

Suggested Fix

When a settings directory is absent at scan time, watch its parent directory for the directory being created (then promote to a full settings-dir watch); or re-run the watch-target scan when the atomic-write staging path first creates a project .claude/.

View original on GitHub ↗