Claude Codeの警告「matches no tool」の意味とdenyルールの直し方
起動バナーの下に出る「Deny rule Bsh matches no tool · check for typos」は、denyやaskのツール名が認識されず、ルールが効いていない合図です。直し方を示します。
Claude Codeの起動バナーの下に ⚠ Deny rule Bsh matches no tool · check for typos と出たら、その Bsh のdenyルールは、どのツールにも当たっていません。拒否したつもりのツールは、拒否されないまま動きます。
この警告はdenyとaskのルールにだけ出ます。ここでは、行の読み方、原因の見分け方、直し方、直ったかどうかの確かめ方を順に示します。
この警告は何を伝えているのか
警告の行は、次の形です。
⚠ Deny rule Bsh matches no tool · check for typos読みどころは3点あります。
Deny ruleの後ろのBshは、あなたが書いたルールそのままの文字列ですmatches no toolは、その名前に対応するツールが存在しないという意味です- 警告であってエラーではないので、Claude Code自体は起動します
怖いのは3点目です。起動できてしまうため、気づかないまま運用に入れます。Bash を止めたいのに Bsh と書いたdenyルールは、何も止めません。
どこに表示されるのか
対話セッションでは、起動バナーの下の1行として出ます。バナーが流れた後で探すより、起動直後に見るのが確実です。
実行モードによって、出る場所と文面が変わります。
| 実行形態 | 出る場所 | 文面 |
|---|---|---|
| 対話セッション | 出る場所起動バナーの下の行 | 文面Deny rule Bsh matches no tool · check for typos |
| 非対話モード(テキスト出力) | 出る場所stderr | 文面Permission deny rule "Bsh" matches no known tool — check for typos. |
バックグラウンドセッション、--output-format json / stream-json | 出る場所デバッグログ | 文面stderrには出ない |
JSONで出力を機械処理するパイプラインでは、stdoutもstderrも汚さないためにデバッグログへ回ります。--debug を付けて起動すると、~/.claude/debug/<session-id>.txt に書かれます。CIで claude -p を回している場合、警告に気づかないまま走り続ける経路がここにあります。
バナーが流れてしまったときはデバッグログを読む
対話セッションの起動時警告は、まず起動バナーの下の行を見ます。そこで見落としたなら、次に見る場所はデバッグログです。
このログはデバッグモードのときだけ残ります。あとから探すのではなく、出力先を指定して起動し直します。
claude --debug-file ./claude-debug.log起動後に、指定したパスのファイルを開いて matches no tool を検索します。ログに残る警告はこの1行だけではないので、同じ起動で出た他の設定警告も並べて確かめられます。~/.claude/debug/<session-id>.txt に書かれる経路は、バックグラウンドセッションやJSON出力の場合の話で、対話セッションでは出力先を自分で決めておくほうが探す手間がありません。
v2.1.305より前は、対話セッションでも長い文面がstderrに出ていました。古いバージョンで見かけたのは、この長い形です。
なぜツール名が認識されないのか
典型は3パターンです。
- 綴りの間違い:
Bsh、Raed、Grpeのような打ち間違い - トランスクリプトの表示名で書いた: 画面に出るラベルは正式なツール名と違うことがあり、公式の例では
Stop Taskと表示されるツールの正式名はTaskStopです - 存在しないツール名を書いた: 実在しない名前は、綴りが合っていても同じ警告になります
2つ目は見落としやすい型です。権限ルールとhookのマッチャーは、表示ラベルには当たりません。Stop Task と書いたルールは、画面で見たとおりの名前なのに無効です。
ルールに書く名前は、ツールリファレンスにある正式名が基準です。たとえば Bash、Edit、Read、WebFetch、PowerShell です。大文字小文字を含め、一覧の表記をそのまま写します。
denyルールのツール名を正式名に直す
警告が出る書き方
{
"permissions": {
"deny": ["Bsh", "Stop Task"]
}
}警告が消える書き方
{
"permissions": {
"deny": ["Bash", "TaskStop"]
}
}警告が出ないケースもある
権限ルールの説明には、警告の対象外が2つ書かれています。
- ツール名に
_や*を含むルールは、検査の対象外です。MCPツールのmcp__github__create_issueや、mcp__*のような名前のglobがこれに当たります - Claude Codeが廃止したツール名も対象外です。例として
TaskOutputが挙げられています
つまり mcp__githb__create_issue のようにMCPのサーバー名を打ち間違えても、この警告では気づけません。MCPツールのルールは、サーバー名とツール名を別の方法で確かめる必要があります。書き方はMCP権限ルールの記事にまとめています。
ツール名のglobを打ち間違えた場合
denyとaskは、ツール名の位置にglobを書けます。パターンはツール名の全体に一致する必要があり、* は全ツール、mcp__* は全MCPツールを指します。
{
"permissions": {
"deny": ["mcp__*", "Bsh*"]
}
}1つ目の mcp__* は意図どおり全MCPツールを止めます。2つ目の Bsh* は Bash* の打ち間違いで、どのツール名にも一致しません。ところが名前に * を含むため、先ほどの検査の対象外になり、matches no tool も出ません。通常の Bsh なら警告で気づけたのに、globにした途端に静かに無効化されます。
globを使うルールは、起動して実際にツールが止まるかを確かめる必要があります。
もう1つ、EndConversation には例外があります。bare名のglobでdenyしても、他のツールが残っているかぎり取り除けません。askのglobも、このツールには確認を出しません。* で全部止めたつもりでも、このツールだけは残ります。
もう1点、警告が出るのはdenyとaskだけです。allowルールのタイポには、この行は出ません。allowのタイポは、承認が省略されないだけで済みます。denyの打ち間違いは、静かに保護を失う点が違います。
タイポのdenyが残ると何が起きるか
具体例で見ます。次の設定は、Bashを丸ごと止めるつもりで書かれています。
{
"permissions": {
"allow": ["Bash(npm run *)"],
"deny": ["Bsh"]
}
}Bsh は未認識なので、denyはどのツールにも当たりません。Bashは取り除かれず、npm run 系のコマンドはallowのとおり承認なしで通ります。それ以外のコマンドも、拒否はされず通常の承認の流れに乗ります。
正しく "deny": ["Bash"] と書けば、裸のツール名のdenyはそのツールをClaudeのコンテキストから取り除きます。Bash(*) も同じ意味です。設計意図が「Bashを使わせない」なら、警告が出た時点で保護がゼロだったことになります。
askルールでも同じ
askは「毎回確認する」ルールです。WebFetch を確認付きにしたくて WebFetc と書くと、確認は入らず、通常の承認の流れに戻ります。確認を挟む前提で運用していた手順が、気づかないうちに外れます。
hookのマッチャーにも同じ落とし穴がある
表示ラベルに当たらないのは権限ルールだけでなく、hookのマッチャーも同じです。PreToolUse のhookで特定のツールだけを検査したいとき、画面で見たラベルを書くとマッチしません。hookのマッチャーは権限ルールと別の設定なので、正式名で書いたかどうかは、実際に一度発火させて確かめるのが確実です。
原因のルールを見つける手順
どのファイルのどのルールかは、/permissions で追えます。
警告の出所を特定する
- 1
セッションで /permissions を開く
ルール一覧が出ます。警告の行に書かれた文字列と同じルールを探します。
- 2
該当ルールを選んで詳細を見る
詳細に出所が書かれています。
From project local settingsのような設定ファイル由来の表示と、CLIの--disallowedToolsで渡した値を示すFrom CLI argumentがあります。 - 3
出所に応じて直す
設定ファイルならその場で正式名に直します。
From enterprise managed settingsと出たら、自分では変更できません。
管理設定由来の場合は、警告の行をそのまま、管理設定の担当者へ転送します。行にはルールの文字列が入っているので、担当者はどのルールか特定できます。
CLIフラグ由来なら、シェルのエイリアスやスクリプトに書いた --disallowedTools の値を確認します。
claude --disallowedTools Bsh直したあとの確かめ方
設定ファイルを正式名に直したら、セッションを開き直します。起動バナーの下から警告の行が消えれば、ツール名が認識されています。
ただし、警告が消えたことは「ツール名が実在する」ことの確認にとどまります。ルールが意図した範囲を止めているかは別の問題です。Bash(git push *) のように指定子を付けたルールは、書き方が合っているかを実際の挙動で確かめます。指定子の書き方は権限ルールのパラメータ指定で扱っています。
評価の順序も押さえておきます。ルールはdeny、ask、allowの順に評価され、最初に当たったものが結果を決めます。denyがask・allowより先に見られるので、正しく書いたdenyは、他のどのallowでも覆りません。逆に、タイポで当たらないdenyは、後ろのallowに素通しされます。
似た警告との切り分け
権限ルールまわりの警告は、原因が違っても似た見た目で出ます。
| 警告 | 原因 | 参考 |
|---|---|---|
matches no tool | 原因denyかaskのツール名が未認識 | 参考この記事 |
Invalid permission rule ... was skipped | 原因括弧が崩れていて、ルールが読み込まれない | 参考括弧の崩れの直し方 |
is not matched by file permission checks | 原因Write や Glob にパスを付けたルール。ファイル権限は Edit と Read で判定される | 参考書き換え先の選び方 |
Agent would be spawned with zero tools | 原因サブエージェントの tools 欄の名前がすべて不一致 | 参考サブエージェントのtools欄 |
最後の行は、tools 欄にも Grpe のような打ち間違いが起こりやすい点で、この警告と原因が似ています。ただし対象は権限ルールではなくサブエージェントの定義で、出る場面も別です。
Write(docs/**) のdenyルールには、matches no tool ではなく never matches · use Edit(docs/**) の行が出ます。ツール名自体は実在するため、この警告の対象にはなりません。
打ち間違いを防ぐ運用
警告を起動時に見つけるだけでなく、書く側でも減らせます。
- ルールを足すときは、ツールリファレンスの名前を写す。画面に出たラベルから起こさない
- チームで共有する
.claude/settings.jsonを変えたPRでは、手元で一度起動して警告の行が出ないことを確かめる - 非対話モードのCIでは、stderrを捨てずにログへ残す
最後の点は実害に直結します。拒否を前提にした自動化で、denyが効いていないことにCIのログからしか気づけない場合があるためです。
まとめ
matches no tool は、denyやaskの名前がどのツールにも結びついていないという通知です。直し方は、正式名への置き換えだけで済みます。
怖いのは、警告がエラーではなく、出る場所も実行形態で変わる点です。対話セッションでは起動バナーの下、非対話ではstderr、JSON出力やバックグラウンドではデバッグログを確認してください。拒否を頼りにしている設定ほど、起動のたびに1行を見る価値があります。