How to Prevent Claude Code from Overwriting settings.json Itself: Verifying Deny Rules and Protected Paths
This page has been translated by machine translation. View original
I love coffee, and I'm emi.
Claude Code's settings.json has a permission rule called deny that prohibits command execution. The detailed mechanism is described in the official documentation below.
I was thinking about whether it was possible to prevent Claude itself from rewriting settings.json when using Claude Code. So I tried to verify whether I could prevent Claude Code itself from rewriting settings.json by writing rules in permissions.deny of settings.json.
I'll introduce what I found out through research and what I confirmed through actual behavior testing.
Conclusion First
1. Denying Edit blocks file operations via Edit
- When
Edit(//**/.claude/settings.json)is added todeny, rewrites via Edit / Write were blocked in all 3 modes I tested - It stops with
Error editing filewithout even showing a prompt
2. Write with Edit, not Write
- File permission checks only target
Edit(path)andRead(path)- Writing
Write(...)is not referenced and a warning is shown at startup
- Writing
Edit(...)applies to all built-in tools that edit files, so writing justEdit(...)covers the Write tool as well
3. .claude is protected as a protected path even without deny, but be careful as behavior changes depending on the permission mode
.claude/settings.jsonis protected as a protected path even without writing deny- However, protected paths behave differently by mode:
autoroutes to the classifier,bypassPermissionsallows it
4. Just denying Edit cannot block operations via Bash
- The
deny'sEdit(...)stops Edit / Write routes, but cannot block file editing via Bash (such assed -ior writing withpython) - If you want to also block operations via Bash, the official documentation recommends adding commands in the form of
Bash(...)todeny, or using a sandbox orPreToolUsehooks that don't depend on command strings (Configure permissions)
Verification Environment
- Claude Code v2.1.248
- Amazon Bedrock (Opus 4.8)
- Test project
~/work/test
This time I experimented using the project-scoped ~/work/test/.claude/settings.json.
What I Was Initially Thinking
At first, I was thinking of listing commands in deny like this.
{
"permissions": {
"deny": [
"<write commands that rewrite settings.json here>"
]
}
}
But I thought it wasn't realistic to cover all the rewrite commands. Also, I found out there are 2 ways Claude can rewrite files.
2 Ways Claude Code Rewrites Files
There are broadly 2 ways Claude Code rewrites files.
- 1. Via Edit
- Uses Claude Code's dedicated built-in file editing tools (Edit / Write)
- 2. Via Bash
- Rewrites files by executing shell commands in the terminal
- Such as
echo '{}' > ~/.claude/settings.json,sed -i 's/old/new/' ~/.claude/settings.json,python3 -c "open('~/.claude/settings.json','w').write('...')", etc.
Writing Edit(...) in the deny rule can block editing via Edit, but it cannot block file editing via Bash command execution, so if you also want to block file editing via Bash, you need to add it separately like Bash(sed:*).
In other words, the approach I initially thought of—listing commands in deny—can only restrict file editing via Bash (and it's practically impossible to cover all cases anyway).
Initial settings.json Configuration
For now, I tried configuring .claude/settings.json as follows.
{
"permissions": {
"deny": [
"Edit(//**/.claude/settings.json)",
"Write(//**/.claude/settings.json)"
]
}
}
The path inside Edit(...) is specified in a way similar to gitignore. The way to specify paths is also described in Read and Edit.
- Starting with
//- Becomes an absolute path from the filesystem root, meaning "anywhere on the machine"
**- A wildcard that matches any number of intermediate folders
So //**/.claude/settings.json means "match any file with the ending .claude/settings.json wherever it is on the machine." This time I wanted to cover both the user-scoped settings.json and the project-scoped settings.json, so I wrote it this way.
Specifically, it matches both of the following.
~/work/test/.claude/settings.json(project scope)~/.claude/settings.json(user scope)
How to Write deny to Reject File Editing
When I started Claude Code on Bedrock, the following message appeared at startup.
emiki@<hostname>:~/work/test$ claude-bedrock
Enter MFA code for <arn>: xxxxxx
Permission deny rule (.claude/settings.json): Write(//**/.claude/settings.json) is not matched by file permission checks — only Edit(path) rules are. Use Edit(//**/.claude/settings.json) instead (Edit rules cover all file-editing tools).
:
emiki@<hostname>:~/work/test$
It says that Write(//**/.claude/settings.json) is not subject to file permission checks. Only Edit(path) rules are subject to permission checks, and since Edit rules cover all file editing tools, please use Edit(//**/.claude/settings.json).
Looking more closely, I found it was also written in the documentation that Claude Code's file permission checks only look at Edit(path) and Read(path) rules.
Edit rules apply to all built-in tools that edit files.
Claude Code only checks file permissions against Edit(path) and Read(path) rules. If you instead write path rules for the Write, NotebookEdit, Glob, or legacy MultiEdit tools, Claude Code will accept the rules but never consult them, and will display a warning at startup.
Since the Write tool is also included in "all built-in tools that edit files," writing just Edit(...) covers writes via the Write tool as well.
When I modified settings.json as follows, the warning no longer appeared.
{
"permissions": {
"deny": [
"Edit(//**/.claude/settings.json)"
]
}
}
Verifying Whether Editing settings.json Is Blocked
I chose cleanupPeriodDays as the key to append. Since it's a number, it's easy to see at a glance whether it was appended, and it's a relatively harmless setting for the number of days to retain chat history. The default is 30 (cleanupPeriodDays). For easy verification of whether it was appended, I'll have 45 appended in the test.
I'll ask Claude Code
Append `"cleanupPeriodDays": 45` to `.claude/settings.json`
and see if it gets rewritten.
Test 1: auto Mode
First I tried it in auto mode.
▐▛███▛█ Claude Code v2.1.248
▝▜██████▀ Opus 4.8 · Amazon Bedrock
▝▝ ▝▝ ~/work/test
❯ Append "cleanupPeriodDays": 45 to .claude/settings.json
Read 1 file
● Update(.claude/settings.json)
Error editing file
The .claude/settings.json itself has a deny rule that prohibits editing this configuration file, and editing has been blocked.
"deny": [
"Edit(//**/.claude/settings.json)"
]
As long as this rule is in effect, I cannot rewrite .claude/settings.json. As a workaround, I could write directly via Bash, but that would bypass the prohibition rule you set, so I won't do it without your permission.
Options for how to proceed:
- Temporarily remove the deny rule (or exclude this file from the target) → then I'll append cleanupPeriodDays: 45
- Append it yourself. If you paste the following into the prompt, it can be executed in this session
! python3 -c "import json,pathlib; p=pathlib.Path('/home/emiki/work/test/.claude/settings.json'); d=json.loads(p.read_text()); d['cleanupPeriodDays']=45; p.write_text(json.dumps(d, indent=2, ensure_ascii=False)+'\n')"
Please let me know how you'd like to proceed.
✻ Sautéed for 29s · done 7:55 AM
※ recap: I want to append cleanupPeriodDays: 45 to .claude/settings.json, but the same file is blocked from editing by a deny rule. Next, waiting for a response on how to proceed: whether to remove the deny rule or append it yourself. (disable recaps in /config)
✘ Auto-update failed · Try claude doctor or npm i -g @anthropic-ai/claude-code
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
⏵⏵ auto mode on (shift+tab to cycle) · ← for agents · shift+click to native select
Claude tried to write with the Update (Edit) tool and editing was blocked by the deny rule, resulting in Error editing file.
In auto mode, it determined that "I could bypass it by writing directly via Bash, but that would bypass the prohibition rule you set, so I won't do it without your permission" and stopped.
Test 2: bypassPermissions Mode
Next, I tried it in bypassPermissions mode, which doesn't ask for any permissions.
This confirms whether deny blocks editing even without the model judgment of auto mode.
A warning appeared at startup.
emiki@<hostname>:~/work/test$ claude-bedrock --permission-mode bypassPermissions
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
WARNING: Claude Code running in Bypass Permissions mode
In Bypass Permissions mode, Claude Code will not ask for your approval before running potentially dangerous commands.
This mode should only be used in a sandboxed container/VM that has restricted internet access and can easily be restored if damaged.
By proceeding, you accept all responsibility for actions taken while running in Bypass Permissions mode.
https://code.claude.com/docs/en/security
❯ No, exit
Yes, I accept
Enter to confirm · Esc to cancel
It says that you're running in bypassPermissions mode, please make sure you're in an environment where problems can occur, and that you agree to bear all responsibility for anything that happens.
This time I nervously selected Yes, I accept and proceeded.
▐▛███▛█ Claude Code v2.1.248
▝▜██████▀ Opus 4.8 · Amazon Bedrock
▝▝ ▝▝ ~/work/test
❯ Append "cleanupPeriodDays": 45 to .claude/settings.json
Read 1 file
● Update(.claude/settings.json)
Error editing file
As expected, a rule prohibiting editing is included in this settings.json itself, and Edit has been blocked.
"deny": [
"Edit(//**/.claude/settings.json)"
]
As long as this rule exists, I (Claude) cannot edit .claude/settings.json. Please handle it with one of the following:
- Open the file yourself and append "cleanupPeriodDays": 45,. After editing, it would look like this:
{
"cleanupPeriodDays": 45,
"permissions": {
"deny": [
"Edit(//**/.claude/settings.json)"
]
}
}
- If you want me to edit it including the deny rule, you first need to remove this rule (since I can't do that either for the same reason, you'll need to manually delete it)
Please decide what to do and I'll proceed accordingly.
✻ Brewed for 17s · done 8:50 AM
✘ Auto-update failed · Try claude doctor or npm i -g @anthropic-ai/claude-code
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
⏵⏵ bypass permissions on (shift+tab to cycle) · ← for agents · shift+click to native select
It stopped properly.
Test 3: acceptEdits Mode
acceptEdits mode is a mode that automatically approves file edits, mkdir, touch, rm, rmdir, mv, cp, and sed (Auto-approve file edits with acceptEdits mode). It's a mode that says "I'll leave it to Claude to use general file editing tools like Edit and common file operation commands like file creation, but please confirm with humans for things like curl and other command executions."
In addition to file edits, acceptEdits mode auto-approves common filesystem Bash commands. mkdir, touch, rm, rmdir, mv, cp, sed.
Since common Bash commands are automatically approved without confirmation, I was going to try it, but while looking at the documentation I found the following description.
Writes to paths outside that scope, writes to protected paths, deletions by rm and rmdir targeting critical paths, and all other Bash commands except the built-in set of read-only commands will still prompt you.
Protected paths…? Critical paths…?
Could it be that settings.json is already protected from the start?
.claude is also a protected path (protected paths)
Claude Code has a mechanism called protected paths, and the .claude directory is included there. Writes are not automatically approved except in modes like bypassPermissions. It's a mechanism to prevent repository state and Claude's own settings from being accidentally broken.
List of protected paths (protected paths)
Protected Directories
| Directory | Notes |
|---|---|
.git |
|
.config/git |
|
.vscode |
|
.idea |
|
.husky |
|
.cargo |
|
.devcontainer |
|
.yarn |
|
.mvn |
|
.claude |
Except .claude/worktrees. Claude stores its own git worktrees here |
Protected Files
| Category | Files |
|---|---|
| git-related | .gitconfig, .gitmodules |
| shell-related | .bashrc, .bash_profile, .bash_login, .bash_aliases, .bash_logout, .zshrc, .zprofile, .zshenv, .zlogin, .zlogout, .profile, .envrc |
| npm / yarn / pnpm / bun related | .npmrc, .yarnrc, .yarnrc.yml, .pnp.cjs, .pnp.loader.mjs, .pnpmfile.cjs, bunfig.toml, .bunfig.toml |
| Bazel-related | .bazelrc, .bazelversion, .bazeliskrc |
| pre-commit / lefthook related | .pre-commit-config.yaml, lefthook.yml, lefthook.yaml, .lefthook.yml, .lefthook.yaml |
| Gradle / Maven related | gradle-wrapper.properties, maven-wrapper.properties |
| devcontainer-related | .devcontainer.json |
| Other tool settings | .ripgreprc, pyrightconfig.json |
| Claude / MCP related | .mcp.json, .claude.json |
Comparing Edit / Write to normal files (within the working directory) with Edit / Write to protected paths (protected paths) is as follows.
What's being compared here is file editing by Edit / Write tools (editing via shell commands like sed or python is handled differently).
| Mode | Edit / Write to normal files Choosing a permission mode |
Edit / Write to protected paths |
|---|---|---|
| default | Prompt shown | Prompt shown |
| acceptEdits | Auto-approved (no confirmation prompt) | Prompt shown |
| auto | Auto-approved (passes through without classifier) | Routed to classifier |
| dontAsk | Rejected | Rejected |
| bypassPermissions | Allowed | Allowed |
※ plan mode is originally a mode that blocks editing of source files, and since explaining its behavior in detail would be lengthy, I won't cover it in this article.
Regarding auto mode, the classifier evaluation order in the official documentation (How the classifier evaluates actions) states that "read-only operations and file edits within the working directory are automatically approved, but writes to protected paths and the first read outside working directories are exceptions that will prompt for confirmation."
Read-only actions and file edits in your working directory are auto-approved, except writes to protected paths and the first read outside the working directories, which prompts you
Everything else goes to the classifier.
Shell commands like sed, echo, and python fall under "everything else," so they go to the classifier even for writes to normal files. The "auto-approved (passes through without classifier)" in the table is only for the Edit / Write case.
In Test 2, for bypassPermissions, protected paths should also be "allowed," but thanks to deny, Edit was properly stopped.
I tried the behavior patterns with and without deny in the permission rules in acceptEdits mode.
With deny
When I asked in acceptEdits (⏵⏵ accept edits on) mode with deny in place, Edit was blocked.
▐▛███▛█ Claude Code v2.1.248
▝▜██████▀ Opus 4.8 · Amazon Bedrock
▝▝ ▝▝ ~/work/test
❯ Append "cleanupPeriodDays": 45 to .claude/settings.json
Read 1 file
● Update(.claude/settings.json)
Error editing file
As expected, editing has been blocked. This settings.json itself has an Edit(//**/.claude/settings.json) deny rule, and I cannot rewrite this file via Edit / Write.
The options are one of the following.
- Execute directly in your terminal (executable from this session with the ! prefix)
! python3 -c "import json,pathlib; p=pathlib.Path('.claude/settings.json'); d=json.loads(p.read_text()); d['cleanupPeriodDays']=45; p.write_text(json.dumps(d, indent=2, ensure_ascii=False)+'\n')"
- If it's okay to temporarily remove the deny rule, delete the relevant line from deny in .claude/settings.json and then request again
The content after appending would look like this:
{
"permissions": {
"deny": [
"Edit(//**/.claude/settings.json)"
]
},
"cleanupPeriodDays": 45
}
Please let me know which way you'd like to proceed.
✻ Cooked for 16s · done 9:34 AM
✘ Auto-update failed · Try claude doctor or npm i -g @anthropic-ai/claude-code
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
⏵⏵ accept edits on (shift+tab to cycle) · ← for agents · shift+click to native select
It shows that it was blocked by deny.
Without deny
Next I emptied deny.
{
"permissions": {
"deny": []
}
}
❯ Append "cleanupPeriodDays": 45 to .claude/settings.json
Since Edit / Write cannot be used due to deny rules, I'll rewrite it from Bash.
● Adding cleanupPeriodDays to settings.json via python
⎿ $ python3 -c "import json,pathlib; p=pathlib.Path('/home/emiki/work/test/.claude/settings.json'); d=json.loads(p.read_text()); d['cleanupPeriodDays']=45; p.write_text(json.dumps(d, indent=2, ensure_ascii=False)+'\n')" && cat /home/emiki/work/test/.claude/settings.json
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Bash command
Tip: auto mode handles these prompts for you — choose "switch to auto mode" below
│ python3 -c "import json,pathlib; p=pathlib.Path('/home/emiki/work/test/.claude/settings.json'); d=json.loads(p.read_text()); d['cleanupPeriodDays']=45; p.write_text(json.dumps(d, indent=2, ensure_ascii=False)+'\n')" && cat /home/emiki/work/test/.claude/settings.json
Add cleanupPeriodDays to settings.json via python
This command requires approval
Do you want to proceed?
❯ 1. Yes
2. Yes, and don't ask again for: python3 *
3. Yes, and switch to auto mode · auto mode handles these prompts for you
4. No
Esc to cancel · Tab to amend · ctrl+e to explain
Hmm…?
It says it can't edit due to the deny rule…
The official documentation When edits take effect says that permissions edits are reflected without a restart, but perhaps they haven't been reflected yet.
Claude Code monitors settings files and reloads them when they change, so most edits, including edits to permissions, hooks, and credential helpers like apiKeyHelper, are applied to the running session without a restart.
Let me stop the operation with No for now and try again.
❯ Append "cleanupPeriodDays": 45 to .claude/settings.json
Since Bash was also stopped, I have no remaining means to rewrite it (Edit / Write blocked by deny rule, Bash just rejected).
Please choose how to proceed.
- Execute directly in your terminal. Paste the following into this input field to execute from this session
! python3 -c "import json,pathlib; p=pathlib.Path('.claude/settings.json'); d=json.loads(p.read_text()); d['cleanupPeriodDays']=45; p.write_text(json.dumps(d, indent=2, ensure_ascii=False)+'\n')"
- Temporarily remove the deny rule. Remove Edit(//**/.claude/settings.json) from deny in .claude/settings.json and request again
- Allow me to rewrite via Bash (approve the command from earlier)
Which would you like?
✻ Sautéed for 11s · done 9:41 AM
✘ Auto-update failed · Try claude doctor or npm i -g @anthropic-ai/claude-code
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
⏵⏵ accept edits on (shift+tab to cycle) · ← for agents · shift+click to native select
Hmm, it's still saying it can't edit because of the deny rule. Let me exit the session with /exit and restart Claude Code in acceptEdits (⏵⏵ accept edits on) mode and try again.
▐▛███▛█ Claude Code v2.1.248
▝▜██████▀ Opus 4.8 · Amazon Bedrock
▝▝ ▝▝ ~/work/test
❯ Append "cleanupPeriodDays": 45 to .claude/settings.json
Read 2 files
● Update(.claude/settings.json)
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Edit file
.claude/settings.json
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
{
"permissions": {
"deny": []
- }
+ },
+ "cleanupPeriodDays": 45
}
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
Do you want to make this edit to settings.json?
❯ 1. Yes
2. Yes, and allow Claude to edit its own settings for this session
3. No
Esc to cancel · Tab to amend
As described in protected paths, the option Yes, and allow Claude to edit its own settings for this session is displayed.
When prompted for writes to .claude/, a Yes, and allow Claude to edit its own settings for this session option is displayed, which approves subsequent .claude/ writes within that session without prompting again.
This time I'll cancel with No.
This confirmed the behavior.
When deny is present, Edit is immediately blocked with Error editing file and no prompt appears. When deny is absent, in acceptEdits mode, Edit is not blocked and a confirmation prompt for the protected path appears.
The official documentation's When edits take effect says that permissions edits are reflected without a restart. This time it didn't seem that way, which is mysterious. As similar reports, I found multiple Issues saying that permission changes in settings.json were not reflected in the running session and a restart was needed (#30737, #33829, #53538). I wasn't able to identify the cause.
What I Found Out
※ Repeated from above
1. Denying Edit blocks file operations via Edit
- When
Edit(//**/.claude/settings.json)is added todeny, rewrites via Edit / Write were blocked in all 3 modes I tested - It stops with
Error editing filewithout even showing a prompt
2. Write ではなく Edit で書く
- The targets for file permission checks are only
Edit(path)andRead(path)- Even if you write
Write(...), it will not be referenced and a warning will appear at startup
- Even if you write
- Since
Edit(...)applies to all built-in tools that edit files, writing justEdit(...)also covers Write tools
3. .claude is protected as a protected path even without a deny, but be aware that behavior changes depending on the permission mode
.claude/settings.jsonis protected as a protected path even without writing a deny- However, behavior for protected paths changes depending on the mode:
autosends it to the classifier, whilebypassPermissionsallows it
4. Simply denying Edit cannot block operations via Bash
denyonEdit(...)only stops access via Edit / Write; it cannot block operations via Bash (such assed -ior writing withpython)- If you also want to block operations via Bash, the official documentation recommends either adding commands in the form of
Bash(...)todeny, or using a sandbox orPreToolUsehook that does not depend on command strings (Configure permissions)
Closing
If you don't want Claude Code to rewrite settings.json, the clearest approach is to add one line with Edit(//**/.claude/settings.json) to deny.
I also discovered that .claude is already slightly protected as a protected path by default.
If you want to prevent edits via Bash as well, the documentation states that writing to deny, or using a sandbox or PreToolUse hook, is effective.
Incidentally, in all the testing I've done so far, the model itself has stopped and said something like "I could update it via bash, but please confirm," so it feels like the model will stop you under normal instructions and normal usage. (I'm not sure what would happen if you used it with instructions like "complete the task no matter what, don't stop working" and let it run unattended in the background.)
I hope this is helpful for anyone else who has been troubled by settings.json being modified without permission.
For questions or requests about this article, please contact us via "DevelopersIOへのご意見" at the bottom of the screen.
