AnswerQA

Why isn't my CLAUDE.md / settings.json taking effect?

Answer

Check three things in order: which file Claude is actually loading (precedence runs user → project → project-local → CLI args → managed), whether your edit is shadowed by a higher layer, and whether the change needs a session restart. Most no-effect cases are precedence, not bugs.

By Kalle Lamminpää Verified May 7, 2026

Most “the change does not take effect” reports are not bugs. Claude Code reads several settings files in a strict order, and the file you edited may be shadowed by a higher-priority layer you forgot existed.

The precedence ladder

settings.json layers load in this order; later layers override earlier ones for scalar keys. Managed settings sit at the top and cannot be overridden by command-line flags.

  1. User: ~/.claude/settings.json
  2. Project: <repo>/.claude/settings.json (committed)
  3. Project local: <repo>/.claude/settings.local.json (gitignored)
  4. Command-line arguments: flags like --model, --permission-mode, --allowed-tools override the three file layers above
  5. Enterprise managed: OS-specific managed-settings file (highest priority; CLI args do not override it)

Scalars (model, defaultMode, apiKeyHelper) follow strict override semantics: the highest layer with the key wins. Permission lists (allow, deny) merge across every layer: every entry contributes, and deny always beats allow. Hooks merge by event type.

CLAUDE.md is layered, not overridden, and gets a few extras most blog posts skip:

  1. Enterprise managed CLAUDE.md (highest)
  2. <repo>/CLAUDE.md or <repo>/.claude/CLAUDE.md (project root)
  3. <repo>/CLAUDE.local.md (gitignored personal overrides for the project)
  4. ~/.claude/CLAUDE.md (user-wide)
  5. Nested CLAUDE.md files in subdirectories (loaded on demand when files inside that subtree are touched)

The first four files load at session start in root-to-cwd order; nested files come in lazily as Claude reads inside their subtree. Everything stacks. This is why a 200-line user CLAUDE.md plus a 2,000-line project CLAUDE.md is a 2,200-line tax on every turn.

Why your edit does not show up

Run this checklist top-to-bottom. Most “broken” config falls out by step 3.

1. Did you edit the file Claude is actually reading?

Use /status inside a session to see active settings sources and any parse errors. /config opens the settings interface itself. Or launch with claude --debug and read the startup log. Confirm the path you edited is in the list of resolved sources. If it is not, you probably edited a file that is not part of the precedence chain.

2. Is the file shadowed by a higher-priority layer?

Common cases:

  • You set permissions.defaultMode to auto in ~/.claude/settings.json, but <repo>/.claude/settings.json sets it to default, which overrides it on this project.
  • Your allow for Bash(git push *) is in user settings, but an enterprise-managed deny for Bash(git push origin main) blocks that specific command. Deny always beats allow.
  • Local works fine; CI fails. The CI runner has its own user-level settings (or none) and never sees your laptop’s ~/.claude/settings.json.
  • You ran claude --permission-mode plan on the command line, which overrides the user/project/local files for that session. Enterprise-managed settings still win over the flag.

3. Does the change need a session restart?

Some settings hot-reload, others do not, and the canonical docs do not enumerate every key. When in doubt, exit (Ctrl-D) and start a fresh session before declaring the setting broken. A fresh session is a 5-second test that rules out half the “did not take effect” reports on the spot.

4. Is the JSON valid?

A trailing comma or unquoted key invalidates the entire file. Claude Code logs a warning but does not crash; the file is silently skipped. Fastest test:

cat ~/.claude/settings.json | jq .
cat .claude/settings.json | jq .

Same for .mcp.json. Any layer that fails to parse is dropped from the precedence chain entirely.

5. Is CLAUDE.md too long to be heard?

A 3,000-line CLAUDE.md drags every turn. Beyond the token cost, the model attends more strongly to the start of the system prompt; rules buried at the end quietly stop having an effect. Keep CLAUDE.md under 200 lines. For longer-form rules, split them into .claude/rules/*.md files. Path-scoped rule files (declaring a paths glob in frontmatter) only load when matching files are read; rule files without paths still load every session, so they do not save you tokens unless you scope them.

Three real templates

Solo developer, one machine. Put everything in user settings (~/.claude/settings.json); project files are optional.

{
  "permissions": {
    "allow": [
      "Bash(npm test*)",
      "Bash(npm run *)",
      "Bash(git status*)",
      "Bash(git diff*)",
      "Bash(git log*)"
    ],
    "deny": [
      "Bash(git push --force*)",
      "Read(.env*)",
      "Read(**/.env*)"
    ]
  },
  "model": "sonnet"
}

Team-shared project. Commit project rules at <repo>/.claude/settings.json; per-developer preferences live in <repo>/.claude/settings.local.json (gitignored).

{
  "permissions": {
    "allow": [
      "Bash(pnpm test*)",
      "Bash(pnpm run *)",
      "Bash(git status*)",
      "Bash(git diff*)"
    ],
    "deny": [
      "Bash(git push --force*)",
      "Bash(git push * main)",
      "Bash(rm -rf *)",
      "Read(.env*)",
      "Read(**/.env*)",
      "Read(**/secrets/**)"
    ]
  },
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format.sh"
          }
        ]
      }
    ]
  }
}

And the matching <repo>/.claude/settings.local.json:

{
  "model": "opus",
  "permissions": {
    "allow": ["Bash(open *)"]
  }
}

model is scalar (the local file’s opus overrides the project’s choice); permissions.allow merges (the team’s list plus your Bash(open *)).

CI / non-interactive. Tight allowlist, plan mode, no surprise commands. Save as the CI runner’s ~/.claude/settings.json.

{
  "permissions": {
    "defaultMode": "plan",
    "allow": [
      "Bash(npm ci)",
      "Bash(npm run lint)",
      "Bash(npm run typecheck)",
      "Bash(git status)"
    ],
    "deny": [
      "Bash(curl *)",
      "Bash(wget *)",
      "Read(.env*)",
      "Read(**/secrets/**)"
    ]
  },
  "model": "sonnet"
}

Plan mode plus a curated allowlist. CI is the place to be paranoid; no human is sitting at the keyboard to approve an interactive prompt.

Footguns

~/.claude/CLAUDE.md reaches every project. A rule like “always run prettier after editing” in user-level CLAUDE.md applies in every repo, including the one where the team already runs prettier in a hook (you run it twice) and the one that uses biome instead (you corrupt the formatter). Keep user CLAUDE.md to rules genuinely about you: preferred terminal, languages you know, name. Project rules belong in project CLAUDE.md.

Project local settings can leak credentials. <repo>/.claude/settings.local.json is gitignored only if your .gitignore actually contains it. Recent Claude Code project init handles this; older repos that adopted Claude Code mid-flight may not. Run git check-ignore .claude/settings.local.json before storing anything sensitive there.

Enterprise managed settings are invisible by default. A managed deny for Bash(curl *) shows up as “Claude refuses to run a normal command” with no on-screen explanation. claude --debug prints which rule fired. If you are stuck in a “why is this denied” loop on a managed laptop, the debug log is the fastest path to “oh, IT denied this”.

Setting keys are case-sensitive and easy to typo. permissions.defaultMode works; permissions.defaultmode is silently ignored, the same as apiKeyHelper vs apikeyhelper. There is no schema validator that errors loudly. After an edit, run claude config (or check /config in-session) and confirm your key shows up under the parsed values. If it does not, it was probably mistyped.

You edited a <repo>/.claude/settings.json but Claude was launched outside that repo. Project-level settings only load when cwd is inside the project tree. If you cd .. and then claude, project rules vanish; you are back to user-level merging only. “Why isn’t my project rule working” is sometimes “because Claude is not running inside the project anymore.”

When NOT to spend the effort

  • You have not reproduced the problem. “Claude is ignoring my CLAUDE.md” is sometimes “Claude did follow it; I just missed the change.” Re-read the diff and the command Claude actually ran before assuming config is broken.
  • You only run Claude in one place. Solo developer, one machine, one repo: you do not need three templates and a precedence chart. One ~/.claude/settings.json is enough; project files are optional.
  • The setting is a per-task preference. “Use opus for this debug session” goes in a launch flag (claude --model opus) or a per-session toggle, not in committed settings that affect the rest of the team.
  • Your team has not agreed on the rule. Committing a rule to <repo>/.claude/settings.json opts everyone in. Get the agreement first; commit second.

Sources

  • Claude Code settings
    Settings.json schema, the precedence chain, and which keys merge across layers vs which override.
  • Manage Claude's memory
    CLAUDE.md hierarchy: project root, parent directories walked up at session start, and user-level (~/.claude/CLAUDE.md). Source for the lazy-load `.claude/rules/` pattern.
  • Configure permissions
    How allow/deny lists merge across precedence layers (deny always wins), and why defaultMode follows the same precedence chain as scalar settings.

Was this helpful?

Read more