[BUG] .claude/settings.json and settings.local.json resolve against literal cwd, not git root — subdirectory launches silently drop all project settings
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, andautoMemoryDirectory(all silently drop from a subdirectory launch). - A local
.claude/settings.local.jsonwith 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-fileevidence (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.
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
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.mdand 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):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:
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, soprojectscope stops silently collapsing intolocalscope 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.I opened a new issue because the three referenced were all closed
On claude-code 2.1.222, it DOES work correctly for settings.local.json, but not for settings.json 🙄