[BUG] .claude/settings.json and settings.local.json resolve against literal cwd, not git root — subdirectory launches silently drop all project settings

Status Open
Reported on v2.1.198
Maintainer reply None cached
Activity 6 comments · opened Jul 3, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet as an open issue
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

.claude/settings.json and .claude/settings.local.json are resolved against the literal cwd, not the git root. When Claude Code is launched from a subdirectory of a repo, all project-scoped settings — hooks, permissions/allow-rules, enabledPlugins, autoMemoryDirectory, etc. — silently disappear, with no error. CLAUDE.md and skills correctly walk up to the repo root in the same session, so this is a settings-specific resolution bug, not a general cwd-discovery issue.

This looks like the same underlying bug as #36793 ("Project settings hooks not loaded when working directory is a subdirectory of the repo") and its duplicate #44278 ("Project-scoped settings don't resolve from subdirectories, making project scope identical to local scope"). Both were closed — #36793 auto-closed 2026-05-30 as inactive (not fixed, not rejected on the merits), #44278 closed as a duplicate of #36793. I'm filing fresh per the bot's own instruction on #36793 ("open a new issue if this is still relevant"), since it still reproduces on a version far newer than the close date.

What's new here vs. #36793 / #44278

  • A debug session ruled out the workspace-trust gate as an alternative explanation via an intervention test — settings from the subdirectory launch stayed unloaded regardless of trust state. So this is not the trust gate; it's pure path resolution happening before or independent of that gate.
  • Confirmed affected by the same root cause: hooks, permissions/allow-rules, enabledPlugins, and autoMemoryDirectory (all silently drop from a subdirectory launch).
  • A local .claude/settings.local.json with an absolute path value does not fix the underlying issue either — the settings file itself isn't found from the subdirectory in the first place, so its contents never come into play.
  • Below is an actually-executed, copy-pasteable repro with real captured output and --debug-file evidence (see "Steps to Reproduce"), not just a description.

What Should Happen?

.claude/settings.json and .claude/settings.local.json should resolve by walking up from the working directory to the nearest git root (the same resolution CLAUDE.md already uses), so all project-scoped config is available from any subdirectory within the project.

Steps to Reproduce

# 1. Create a minimal repo with a project-level hook that blocks edits
mkdir settings-cwd-repro && cd settings-cwd-repro
git init -q

mkdir .claude subdir
cat > .claude/settings.json <<'EOF'
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "echo 'HOOK FIRED' >&2; exit 2" }
        ]
      }
    ]
  }
}
EOF
echo hello > file.txt
echo hello > subdir/file.txt
git add -A && git commit -q -m "init repro"

# 2. From the repo root — the hook fires and blocks the edit (WORKS)
claude --permission-mode acceptEdits --allowedTools "Edit" -p \
  "Edit file.txt to say 'modified'. Report verbatim whether it succeeded or was blocked."

# 3. From the subdirectory — same repo, same committed settings.json (FAILS)
cd subdir
claude --permission-mode acceptEdits --allowedTools "Edit" -p \
  "Edit file.txt to say 'modified'. Report verbatim whether it succeeded or was blocked."

I ran this exact script on v2.1.198. Actual output:

Step 2 (repo root):

The edit was blocked. Verbatim tool error returned: `` PreToolUse:Edit hook error: [echo 'HOOK FIRED' >&2; exit 2]: HOOK FIRED ``

Step 3 (subdirectory):

Edit result (verbatim): The edit tool call succeeded, no hook blocked it. ... no .claude/settings*.json exist in this project directory

That last line is the subagent itself confirming the divergence — from the subdirectory it can't find .claude/settings.json at all, because it's looking in the wrong place.

Direct confirmation via --debug-file on the same two runs — the line the harness logs at startup, listing every settings file it will watch for changes:

From the repo root:

[DEBUG] Watching for changes in setting files /home/elijah/.claude/settings.json, /path/to/settings-cwd-repro/.claude/settings.json, /path/to/settings-cwd-repro/.claude/settings.local.json...

From subdir/:

[DEBUG] Watching for changes in setting files /home/elijah/.claude/settings.json...

From the subdirectory, the repo's own .claude/settings.json and .claude/settings.local.json never even enter the watch list — the resolver looks for subdir/.claude/settings.json (confirmed via the adjacent "Broken symlink or missing file encountered for settings.json at path: .../subdir/.claude/settings.json" debug line), finds nothing, and silently gives up instead of walking up to the git root the way CLAUDE.md resolution does.

Claude Model

N/A (harness-level bug, not model-dependent)

Is this a regression?

Unclear — #36793 reports this as a regression from a state that worked "confirmed working ~March 6, 2026," but I don't have version-pinned before/after evidence myself.

Claude Code Version

2.1.198

Platform

Anthropic API

Operating System

Linux

Terminal/Shell

fish

Additional Information

Workaround: always launch claude from the repo root — e.g. a shell wrapper that cds to ` git rev-parse --show-toplevel first before invoking claude`.

Related/likely-same-family issues: #36793, #44278, #57563, #55973.

View original on GitHub ↗

4 Comments

github-actions[bot] · 1 month ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/36793
  2. https://github.com/anthropics/claude-code/issues/55973
  3. https://github.com/anthropics/claude-code/issues/8810

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

yurukusa · 1 month ago

The part that makes this worse than a config-discovery quirk is the safety silence: when the guard rules and allow-lists silently drop, any protection you rely on (a pre-action block on destructive commands, a deny rule, an audit step) is just off — and nothing says so. You get a session that looks identical to a protected one but has no guardrails. Anyone launching from a package subdir in a monorepo is exposed to exactly the class of accident those guards exist to stop, while believing they're covered. That's the real severity here, on top of the resolution bug itself.

Two things that hold up until the fix lands (the fix being: resolve project config by walking up to the git root, the same way CLAUDE.md and skills already do):

1. Launch from the git root, always. A trivial wrapper removes the footgun — from any subdir it resolves and cd's to the root first. Verified from a deep subdir (a/b/c → returns the repo root):

# ccroot: launch from the repo root so project config loads
root="$(git rev-parse --show-toplevel 2>/dev/null)" && cd "$root"; exec claude "$@"

2. Detect the drop before it bites. If you can't guarantee the launch dir, a one-shot check warns when you're in a subdir whose config would be dropped (root has the project config, cwd doesn't, cwd ≠ root). Tested logic:

root="$(git rev-parse --show-toplevel 2>/dev/null)"
if [ -n "$root" ] && [ "$PWD" != "$root" ] \
   && [ ! -e "$PWD/.claude/settings.json" ] && [ ! -e "$PWD/.claude/settings.local.json" ] \
   && { [ -e "$root/.claude/settings.json" ] || [ -e "$root/.claude/settings.local.json" ]; }; then
  echo "WARNING: launching here drops $root project config (guards/allow-lists/plugins) — see anthropics/claude-code#74023" >&2
fi

Honest limits: both are workarounds, not fixes — #1 changes your launch habit, #2 only warns (and can't run as a project-scoped guard, since the very thing that would run is what's being dropped; it belongs in your shell rc or a global/user-level entry point). The durable fix is server-side: config resolution should walk to the git root like CLAUDE.md/skills do, so project scope stops silently collapsing into local scope from a subdirectory. Given #36793 and #44278 both closed unresolved, the silent-safety-degradation framing is worth keeping front-and-center — a dropped guard is a security regression, not just a missing preference.

ElijahLynn · 1 month ago
To prevent auto-closure, add a comment or 👎 this comment

I opened a new issue because the three referenced were all closed

st-- · 23 days ago

On claude-code 2.1.222, it DOES work correctly for settings.local.json, but not for settings.json 🙄

Showing cached comments. Read the full discussion on GitHub ↗