Claude Media
Claude Code「Invalid permission rule」の直し方 — 括弧の崩れでスキップ

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. 1

    ファイルとキーを特定する

    claude doctor の Invalid settings 節で、ファイルのパスと permissions.allow などのキー名を読みます。

  2. 2

    ルールを「最後が閉じ括弧」の形に書き直す

    Bash(ls) x なら、意図が「ls に続く任意の引数を許可」のとき Bash(ls *) に直します。括弧が欠けているものは、閉じ括弧を足します。

  3. 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まで読み込まれない例が公式に載っています。権限ルールの括弧の崩れとは別の話です。

この記事を共有:XはてブLinkedIn