Claude Code「Invalid permission rule」の直し方 — 括弧の崩れでスキップ
「Invalid permission rule ... was skipped: Malformed Tool(content) rule」は、権限ルールの括弧の崩れでルールが読み込まれない合図です。見つけ方と直し方を示します。
Invalid permission rule "Bash(ls) x" was skipped: Malformed Tool(content) rule と出たら、その権限ルールは読み込まれていません。allowに書いたなら承認は省略されず、denyに書いたなら拒否が成立しません。
原因は、ルールが Tool または Tool(content) の形から外れていることです。閉じ括弧の後ろに文字が続いている、括弧が片方欠けている、といった崩れが該当します。ここでは、メッセージの読み方、該当するルールの見分け方、直し方、直したかどうかの確かめ方を順に示します。
このメッセージは何を伝えているのか
メッセージ全文は次の形です。
Invalid permission rule "Bash(ls) x" was skipped: Malformed Tool(content) rule. Rules take the form Tool or Tool(content) and must end at the closing ")"; parentheses inside the content are literal読みどころは3点あります。
- 引用符の中が、スキップされたルールそのものです
was skippedなので、設定ファイル全体ではなくそのルール1行だけが落ちています- 後半の
must end at the closing ")"が、直し方の答えです。ルールは最後の閉じ括弧で終わらなければなりません
権限ルールの基本形は Tool か Tool(specifier) の2通りです。ツール名だけを書けばそのツールの全使用に当たり、括弧を付ければ引数で絞れます。この形から外れた行が、このメッセージの対象です。
どんな書き方が弾かれるのか
手元のClaude Code v2.1.296で、空の設定ディレクトリを使い、次の .claude/settings.json を置いて確かめました。
{
"permissions": {
"allow": [
"Bash(ls) x",
"Bash(npm run *",
"Bash(git status *)",
"Edit(./Finance (2024)/**)"
]
}
}4本のうちスキップされたのは先頭の2本だけです。
| ルール | 結果 | 崩れの中身 |
|---|---|---|
Bash(ls) x | 結果スキップ | 崩れの中身閉じ括弧の後ろに x が続いている |
Bash(npm run * | 結果スキップ | 崩れの中身閉じ括弧が欠けている |
Bash(git status *) | 結果有効 | 崩れの中身形どおり |
Edit(./Finance (2024)/**) | 結果有効 | 崩れの中身括弧が中身に含まれるが、最後が ) で終わる |
最後の行は見落としやすい点です。パスやコマンドの中に括弧があること自体は問題になりません。括弧の入れ子や数のバランスは見られておらず、中身の括弧はそのまま文字として扱われます。そのため Finance (2024) のようなフォルダ名に、エスケープは要りません。
見るべき条件は「ルール全体が最後の ) で終わっているか」です。
どこに出るのか — 3つの見つけ方
メッセージに出会う経路は3つあります。
- 対話セッションの起動時に出るinvalid-settingsダイアログ
- ターミナルで実行する
claude doctorの出力 - セッション内の
/doctor
claude doctor はセッションを始めずに、インストールと設定の診断を読み取り専用で出力します。修正案の提示と適用まで求めるなら、セッション内の /doctor を使います。
claude doctor の Invalid settings 節には、どのファイルのどのキーかも並びます。先ほどの例では次のように出ました(パスは ~ に置き換えています)。
Invalid settings
- ~/proj/.claude/settings.json › permissions.allow: Invalid permission rule "Bash(ls) x" was skipped: Malformed Tool(content) rule. Rules take the form Tool or Tool(content) and must end at the closing ")"; parentheses inside the content are literal
- ~/proj/.claude/settings.json › permissions.allow: Invalid permission rule "Bash(npm run *" was skipped: Malformed Tool(content) rule. Rules take the form Tool or Tool(content) and must end at the closing ")"; parentheses inside the content are literal行頭のパスが、直すべきファイルです。有効だった2本は一覧に出ません。一覧に載るのは弾かれた行だけなので、載っていない行は少なくともこの理由では落ちていません。
設定ファイルが複数あるとき、どのファイルを直すか
権限ルールはmanaged、ローカル、プロジェクト、ユーザーの各スコープにまたがって置けます。設定はマージされ、managedが最優先で、設定ファイルどうしではローカル、プロジェクト、ユーザーの順に近いほうが勝ちます。その間に入る claude --settings のコマンドライン指定は、managedの次に強い層で、そのセッションだけに効きます。denyはどのスコープにあってもallowより先に評価されるので、ユーザー設定のallowをプロジェクト設定のdenyが止める、という動きが成り立ちます。
この仕組みと「ルール1本だけがスキップされる」性質が組み合わさると、見落としが生まれます。たとえばプロジェクトの .claude/settings.json に書いたdenyが崩れてスキップされると、ユーザー設定のallowを打ち消す役が消えます。denyを置いたスコープと、それに対するallowを置いたスコープは、別々のファイルでも一緒に疑います。
直せるかどうかもスコープで変わります。
- ユーザー、プロジェクト、ローカルの設定は、自分で開いて書き換えられます
- managedの権限ルールは、コマンドライン引数でも上書きできません。崩れたルールがmanaged由来なら、直すのは配布元の管理者です
- 自分の側でできるのは、
--disallowedToolsでdenyを足すところまでです。managedのdenyを--allowedToolsで覆すことはできません
どのスコープのどのファイルが由来かは、claude doctor の行頭のパスで分かります。セッション内なら /status が、どの設定ソースが有効かを示します。
直し方の手順
スキップされたルールを直す流れ
- 1
ファイルとキーを特定する
claude doctorのInvalid settings節で、ファイルのパスとpermissions.allowなどのキー名を読みます。 - 2
ルールを「最後が閉じ括弧」の形に書き直す
Bash(ls) xなら、意図が「lsに続く任意の引数を許可」のときBash(ls *)に直します。括弧が欠けているものは、閉じ括弧を足します。 - 3
診断をもう一度走らせる
同じコマンドで、該当の行が消えたことを確かめます。
claude doctor直すときに迷うのは、崩れたルールの「本来の意図」です。Bash(ls) x を書いた人は、おそらく ls x だけを許可したかったのか、ls の後ろに何か続くコマンドを許可したかったのかのどちらかです。前者は括弧の中に入れて Bash(ls x)、後者は Bash(ls *) が形に合います。どちらもルールの意味が変わるので、意図を決めてから書き換えます。
Bashルールの * の置き場所には別の決まりがあります。空白の有無で一致範囲が変わり、Bash(ls *) は lsof に当たらず、Bash(ls*) は当たります。書き換え後の挙動はClaude CodeのBash権限ルールは複合コマンドをどう評価するかも合わせて確認できます。
スキップされると何が起きるか
スキップされたルールは、読み込まれなかったものとして扱われます。権限判定はdeny、ask、allowの順に評価され、最初に当たったものが結果を決めます。そのため、崩れたルールが担っていたはずの役割が、そっくり抜けます。
| 崩れていたルール | 起きること |
|---|---|
| allow | 起きること承認が省略されず、許可したつもりのコマンドでも毎回プロンプトが出る |
| ask | 起きること確認を強制できず、allowに当たるコマンドは確認なしで通る(当たらなければ既定の挙動) |
| deny | 起きること拒否が成立せず、他のルールや権限モード次第で実行できてしまう |
allowが崩れた場合は、プロンプトが出続けるので気づけます。気づきにくいのはdenyの側です。拒否したつもりのルールが抜けていても、通常の作業では何も起きないため、必要になる場面まで発覚しません。Claudeに「触らないで」と伝えても拒否は成立しません。権限ルールはClaude Codeが強制するもので、プロンプトや CLAUDE.md の指示は強制力を持たないためです。
denyを書いたら、claude doctor の Invalid settings に自分のルールが載っていないことまで確認すると、書いただけで終わらせずに済みます。
同じ見た目で、スキップされない警告との切り分け
権限ルールの警告には、ルールを捨てるものと、残すものがあります。起動時に似たような警告が並ぶので、扱いの違いを並べます。
| 警告の中身 | ルールの扱い | 直し方 |
|---|---|---|
Malformed Tool(content) rule | ルールの扱いスキップ(効かない) | 直し方最後が ) で終わる形に直す |
括弧付きの mcp__ ルール | ルールの扱いスキップ(効かない) | 直し方括弧を外すか、--disallowedTools で渡す |
Write(docs/**) のような Write・NotebookEdit・Glob のパスルール | ルールの扱い残る(ただしファイル権限の判定では参照されない) | 直し方Edit(path) や Read(path) に置き換える |
* が後続の語より前にあるBashのallow | ルールの扱い残る(一致範囲が広い) | 直し方* を具体値にするか、サブコマンドの後ろへ移す |
Bash(command:rm *) のような主たる入力フィールドの指定 | ルールの扱い無視され、起動時に警告 | 直し方Bash(rm *) と書く |
括弧付きの mcp__ ルールは、括弧の形が正しくてもスキップされます。MCPツールには引数で絞る記法が使えないためで、詳しくはClaude Code MCP権限ルール — server:tool単位の書き方にまとめています。パス記法の取り違えは別の問題で、/path の起点の違いはClaude CodeのReadルールのパス起点で扱っています。
プロジェクトの .claude/settings.json に書いたallowが効かない原因は、括弧の崩れだけではありません。プロジェクトの permissions.allow は、そのフォルダのワークスペース信頼ダイアログを承認するまで適用されません。claude doctor に崩れた行が出ていないのにallowが効かないときは、信頼の承認が済んでいるかも確かめます。なおdenyとaskは、制限を足すだけなので信頼の承認に左右されません。
以前のバージョンではどう見えたか
挙動はバージョンで変わっています。
- 閉じ括弧の後ろに文字が続くルール(
Bash(ls) xなど)は、何にも一致しないまま黙って無視されていました。v2.1.260で、invalidな設定として報告されるようになっています - 括弧がそろっていないルールのメッセージは、v2.1.260より前は
Mismatched parenthesesでした
古い記事や検索結果で Mismatched parentheses を見かけたら、同じ種類の崩れの旧い表記です。逆に、v2.1.260より前の環境では、崩れたルールがあっても警告が出ないことがあります。更新したら急に警告が並ぶようになった場合、ルール自体は以前から効いていなかったと読めます。更新で壊れたのではなく、見えるようになったのです。
書いてしまってから気づかないための簡易チェック
claude doctor を毎回回すのが手間なら、設定ファイルの権限ルールを機械的に眺める方法があります。次の jq と grep は、ルールが「括弧なし」または「最後が ) で終わる括弧付き」の形かどうかだけを見る簡易チェックです。公式の判定ロジックではないので、見逃しや過検出はありえます。最終判断は claude doctor の出力です。
jq -r '(.permissions.allow // []) + (.permissions.ask // [])
+ (.permissions.deny // []) | .[]' .claude/settings.json \
| grep -vE '^[^()]+(\(.+\))?$'何も出力されなければ、形の崩れは見つかっていません。出力された行が、Bash(ls) x のように最後が ) でないものや、括弧が欠けたものです。ツール名の部分に括弧を含む特殊な書き方は想定していないので、そのような行は別途目視します。
チームで .claude/settings.json をコミットしているなら、レビューのときに /permissions で実際に読み込まれたルールの一覧と見比べる運用も有効です。ダイアログには、どの settings.json 由来のルールかも並びます。使い方はClaude Codeの/permissionsコマンドで権限ルールとauto mode拒否を管理するにあります。
よくある質問
1本崩れると、設定ファイルのほかの設定も無効になりますか
メッセージは was skipped と、ルール1本を指しています。先ほどの例でも、同じファイルの Bash(git status *) と Edit(./Finance (2024)/**) は有効なままでした。なお別の設定項目、たとえば配列で書いたhooksの matcher のように、崩れ方によってはファイルの他のhooksまで読み込まれない例が公式に載っています。権限ルールの括弧の崩れとは別の話です。