「Read deny rule」エラーの対処 — Claude Code
Claude CodeのEdit・Writeが「Read deny rule」で止まる原因は、Readのdenyルールが同じパスのEditとWriteも塞ぐ仕様だからです。症状別の切り分けとNotebookEditが対象外の点も扱います。
.envやsecrets/をReadのdenyルールで守っているプロジェクトがあります。そのファイルをClaude Codeに編集させようとすると「File is covered by a Read deny rule」で止まります。原因は単純です。Readのdenyルールは、読み取りだけでなく同じパスのEditとWriteも一緒に塞ぐ仕様になっているためです。設定ミスではなく、意図された安全策として働いています。
どの症状かで見る対処の分かれ道
エラーの出方によって、見る場所が変わります。次の3手順で原因を絞り込めます。
エラーが出たときの切り分け
- 1
エラー文言を確認する
冒頭が
File is covered by a Read deny ruleなら、このページの話です。Writeでは末尾がand cannot be writtenになります。別の文言なら別の原因です。 - 2
止まったパスを突き合わせる
/permissionsを開き、Readのdenyルールのうち、止まったパスに一致するものを探します。ルールを定義しているsettings.jsonのパスも一覧に出ます。 - 3
バージョンを見る
claude --versionで確認します。v2.1.228より前なら、挙動が変わった経緯を次のバージョンの節で確かめます。
File is covered by a Read deny ruleは何のエラーか
表示される文言は次のとおりです。
File is covered by a Read deny rule in your permission settings and cannot be edited.EditツールとWriteツールは、どちらも書き込んだ内容をClaude自身が読み返せる必要があります。読み取りそのものをReadのdenyルールで塞いでいる以上、書き込みだけを許すと「書いたのに読めない」という矛盾した状態になります。そのためClaude Codeは、ファイルへ一切アクセスする前に呼び出しごと拒否します。新規ファイルの作成であっても、そのパスがReadのdenyルールに一致すれば同じように弾かれます。
なぜReadのルール1つでEditとWriteの両方が止まるのか
多くの人が最初につまずくのはここです。「読み取りを禁止しただけなのに、なぜ編集まで止まるのか」という疑問は自然です。
Claude Codeのパーミッションチェックは、ファイルツールに対してEdit(path)とRead(path)の2種類のルールしか参照しません。WriteやNotebookEdit、MultiEdit向けにパスルールを書いても、ルールとして受理はするものの判定には使われません。書くべきは常にEdit(docs/**)であり、Write(docs/**)ではないという点が、このエラーの背景にある設計です。
つまりReadのdenyルールは「読み取り拒否」と「編集拒否」の二役を1つのルールで兼ねています。ルールを2つに分けて書く必要はなく、Read(./secrets/**)と書いた時点で、そのパスへのEditとWriteも自動的に塞がれます。
バージョンによる挙動の違い
このガードは段階的に強化されてきました。古いバージョンでは、現在の挙動を前提にした設定が想定どおりに動かないことがあります。
Readのdenyルールが塞ぐ範囲の広がり
- v2.1.208より前Editを止めるのはEditのdenyルールだけ
Readのdenyルールを書いても、EditもWriteも通ります。 - v2.1.208以降ReadのdenyルールがEditも止める
Writeはまだ止まりません。
- v2.1.228以降ReadのdenyルールがWriteも止める
現行の挙動です。EditもWriteも、
Readのdenyルールに一致するパスでは拒否されます。
古いバージョンで「Readにdenyを書いたのにEditやWriteが通ってしまう」場合、まず疑うべきは設定ではなくバージョンです。claude updateで最新化すれば、追加設定なしにガードが効きます。
NotebookEditだけは対象外
ReadのdenyルールがEdit・Writeを塞ぐ一方で、NotebookEditツールはこのガードの対象外です。Jupyter Notebook(.ipynb)を編集専用ツールで直接書き換える経路は、Readのdenyルールだけでは止まりません。ノートブックを含むディレクトリを本当に触らせたくない場合は、Readのdenyルールに加えてEditのdenyルールも同じパスへ足す必要があります。
denyルールの範囲を狭めるときの深さ規則
編集させたいときは、/permissionsかsettings.jsonのpermissions.denyで該当パスのReadルールを削るか狭めます。狭めるときは、denyルールだけが持つマッチの深さに注意が必要です。Read(secrets/**)のようにディレクトリ名1つだけを含む相対パターンは、denyとaskでは現在のディレクトリ以下のどの深さにあるsecretsにも一致します。allowルールでは直下のsecretsにしか一致しません。入れ子のパッケージにある同名ディレクトリまで巻き込まれて止まっているなら、原因はこの深さの違いかもしれません。モノレポで特定のパッケージだけを保護したいときは、Read(packages/billing/**)のように途中のパスまで書くと、その位置にしか一致しません。
機密ファイルを守る設定として公式ドキュメントが挙げているのは、次の形です(公式の例では、同じ配列にBash(curl *)も並びます)。
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./config/credentials.json)"
]
}
}このパターンを置くと、一致するファイルはファイル探索と検索結果から外れ、読み取りが拒否され、EditとWriteも止まります。1つのキーで足りる点がpermissions.denyの特徴です。
Grep・Glob・@fileメンション・接続中のIDEが渡す選択範囲への適用は、公式でも「best-effort」とされています。
allowルールを書いても止まる理由
Edit: allowのような許可ルールを別途書いていても、このエラーは解消しません。ルールはdeny → ask → allowの順に評価され、最初に一致したルールが結果を決めます。具体性の高さで順序は変わりません。
Bash(aws *)のような広いdenyルールは、より具体的なBash(aws s3 ls)のallowルールを上書きします。同じ関係が、ReadのdenyルールとEditのallowルールの間にも成り立ちます。denyルールは許可の例外を持てないため、Editを通したいなら許可を足すのではなく、denyルール自体を削るか狭める以外に道はありません。
WriteやGlobにパスルールを書いてしまう間違い
パーミッション設定でありがちな誤りが、Write(path)やGlob(path)にルールを直接書くパターンです。Claude Codeはこれらのルールを受理しますが、ファイルアクセスの判定では一切参照しません。v2.1.210以降は、設定ファイル・管理設定・--allowedToolsなどのフラグ値で見つけると、起動時に次の形の警告を出します。
Permission deny rule (.claude/settings.json): Write(docs/**) is not matched by file permission checks — only Edit(path) rules are. Use Edit(docs/**) instead (Edit rules cover all file-editing tools).警告は括弧内に、問題のルールがどの設定ファイルにあるかまで示します。バックグラウンドセッションや--output-format json・stream-jsonでは、警告が標準エラー出力ではなくデバッグログに書かれます。見落としたまま「ルールを書いたのに効かない」と思い込みやすいのはこのためです。--debugを付けて起動すれば、~/.claude/debug/<セッションID>.txtに残ります。
対応表は次のとおりです。
| 書いてしまいがちなルール | 実際に書くべきルール |
|---|---|
Write(docs/**) | 実際に書くべきルールEdit(docs/**) |
NotebookEdit(docs/**) | 実際に書くべきルールEdit(docs/**) |
MultiEdit(docs/**)(レガシー) | 実際に書くべきルールEdit(docs/**) |
Glob(docs/**) | 実際に書くべきルールRead(docs/**) |
例外は--allowedTools経由で渡すGlobルールのみで、これだけは警告なしでコマンドライン引数として受け付けられます。パスを持たないWriteやGlobの素のルールは、ツール単位で一致するため警告の対象外です。
.claudeignoreと旧ignorePatterns設定は効かない
他のツールの感覚で.claudeignoreを置いても、Claude Codeの動作には影響しません。エントリーはReadのdenyルールに移す必要があります。機密ファイル除外の設定は以前ignorePatternsというキーで書かれていましたが、現在のpermissions.denyはこの非推奨のキーを置き換えたものです。
古いignorePatternsを使っているプロジェクトを引き継いだ場合は、permissions.denyへの書き換えが必要になります。Readルールを書けば、読み取りの拒否に加えて、本記事のテーマであるEditとWriteの拒否まで同時に効きます。
Bash経由のファイルアクセスとの違い
ReadとEditのdenyルールが効く範囲と、効かない範囲を並べると次のとおりです。
denyルールが効く経路と効かない経路
効く
組み込みのファイルツールと、Bashで実行されるcat・head・tail・sed・teeのような「Claude Codeが認識しているファイルコマンド」です。> fileや< fileのようなリダイレクトの宛先も対象です。
効かない
ファイル名を示さずに読むgrep -r pattern .のようなコマンドや、Pythonスクリプトやnode.jsのプログラムが内部でファイルを開く経路です。全プロセスからのアクセスを止めたいなら、denyルールを置いたうえでサンドボックスを有効にします。同じパスがOSレベルでも塞がれます。
v2.1.259だけで起きたReadのdenyルール起因の承認待ち
v2.1.259では、Readのdenyルールを置いた環境で、ファイル編集と関係のないBashコマンドが承認待ちになる退行がありました。
GitHubのissue 91853は、.envと.npmrcを守る環境でcd <dir> && grep ... <相対グロブ>の形のコマンドが毎回止まると報告しています。画面には、cdのあとに検索する先を判断できず、Read()のdenyルールがあるため承認が必要だという趣旨の説明が出ます。issue 91683は、同じ確認がbypassPermissionsモードでも出ると報告しています。
原因は、ReadのdenyルールをBashの引数にも適用する変更でした。この変更は翌日のv2.1.260で撤回されています。issueはopenのままですが、changelog上は解消済みです。
v2.1.259のまま使っているなら、claude updateで解消します。経緯の詳細はClaude Codeのauto modeでcd付きコマンドが確認に落ちる件にあります。
使い分け早見表
| やりたいこと | 設定 | 効果 |
|---|---|---|
| Claudeに読ませない | 設定Read(path)のみ | 効果読み取り・Edit・Write(v2.1.228以降)を拒否。NotebookEditは通る |
| Claudeに一切触らせない | 設定Read(path) + Edit(path) | 効果読み取り・Edit・Write・NotebookEditすべてを拒否 |
| 特定パスだけ範囲を狭める | 設定Read(./secrets/**)をより具体的なパスへ書き換え | 効果意図しない広範囲ブロックを避けられる |
| Bash経由の読み取りもOSレベルで塞ぐ | 設定サンドボックス設定 | 効果Bashなどで起動したコマンドとその子プロセスからのアクセスも遮断 |
よくある質問
.gitignoreに書いたファイルは自動的に保護されますか
されません。.gitignoreはGitの追跡対象外を決めるだけで、Claude Codeのパーミッションとは無関係です。機密ファイルを守るにはpermissions.denyにReadのルールを明示的に書く必要があります。
Readのdenyルールをユーザー設定(~/.claude/settings.json)に書いても効きますか
効きますが、パスの書き方に注意が必要です。ユーザー設定側のRead(/secrets/**)は~/.claude/secrets/**を指し、プロジェクト内のsecretsディレクトリは対象になりません。プロジェクトをまたいで効かせたいなら、//から始まる絶対パスか~/から始まるホーム相対パスで書きます。
symlink経由でファイルにアクセスされた場合はどうなりますか
denyルールは、要求されたパスと、それが指す先のファイルの両方を調べ、どちらかが一致すれば拒否します。allowルールは両方が一致したときだけ適用されるので、denyのほうが厳しく働きます。たとえば./project/keyが~/.ssh/id_rsaを指すsymlinkで、~/.ssh/**がdenyなら、./project/をallowしていても止まります。
PreToolUseフックで同じことはできませんか
できますが、性質が違います。PreToolUseフックはツール呼び出しごとに任意のスクリプトを走らせて動的に許可・拒否を判定する仕組みで、条件分岐や外部連携を組み込めます。一方Readのdenyルールは、パスパターンに対して静的に決まる単純な拒否です。CI設定ファイルや.git配下のような「常に触らせたくない」パスにはdenyルールで十分で、状況によって判断を変えたい場合だけフックを組み合わせます。なお、フックが"allow"を返しても、一致するdenyルールがあれば拒否が優先される点は共通しています。
まとめ
止まっている原因が、一致するReadルールの深さなのか、symlinkの先なのか、Bash経由の別経路なのかは、/permissionsの一致ルールと止まったパスを突き合わせると見分けられます。パーミッション設計全体を見直すならClaude Codeセキュリティ・権限ガイドが参考になります。書式はClaude Code settings.json完全ガイドにあります。