Claude Code modが動かないときの切り分け — 症状別の原因とデバッグログ
Claude Code modが何もしないとき、読み込めない・hookがスキップされる・ツール呼び出しが拒否される・描画されない・編集が消える、の5症状をメッセージ別に切り分ける手順です。
Claude Code modが動かないときは、まず「modそのものが読み込まれたか」を確かめます。modのモジュールやhookが失敗すると、Claude Codeはそれを飛ばしてセッションを続けるため、壊れたmodは「何もしないmod」に見えるからです。手がかりは、modの名前を含む1行のメッセージです。その行を読む場所と、メッセージごとの原因を症状別にまとめます。
症状とメッセージの逆引き
見えている症状から、探すメッセージと出る場所を引きます。右端の列は、原因を詳しく書いた節です。
| 見えている症状 | 探すメッセージ | 出る場所 | 詳細 |
|---|---|---|---|
mods activeに名前がない | 探すメッセージnot loaded: | 出る場所debugログ | 詳細症状1 |
| 組織のポリシーでmodが拒否される | 探すメッセージallowManagedModsOnly | 出る場所debugログ。ホットリロード中はtranscriptにも | 詳細症状1 |
| 特定のhookだけ動かない | 探すメッセージhook skipped: | 出る場所同じ種類の失敗はリロードまで1回だけ表示。全件はdebugログ | 詳細症状2 |
| 登録したコマンドが答えない | 探すメッセージno command.run hook answered it | 出る場所コマンド実行時の返答 | 詳細症状2 |
| 組み込み以外のmodがすべて外れた | 探すメッセージmods that run in the hooks worker are off for this session | 出る場所すべての対話セッションのtranscript | 詳細症状2 |
| ツール呼び出しが拒否される | 探すメッセージa hook changed this call's input | 出る場所拒否理由としてClaudeに伝わる | 詳細症状3 |
| denyルールを越えようとして拒否される | 探すメッセージtried to lift a deny rule in your settings | 出る場所transcriptとdebugログ(claude -pはdebugログのみ) | 詳細症状3 |
| ペインやバンドが空のまま | 探すメッセージui.render (Pane) refused: | 出る場所--plugin-dirならtranscript。debugログではa hook returned a tree that does not validate | 詳細症状4 |
| トーストが見えない | 探すメッセージ$.ui.toastの行 | 出る場所debugログ | 詳細症状4 |
| 直したのに変わらない | 探すメッセージreload failed, the previous version stays loaded: | 出る場所--plugin-dirで編集中のtranscript | 詳細症状5 |
最初の4手 — 読み込み状態を確かめる
原因を探す前に、次の4つを順に試すと、問題が「読み込み前」か「読み込み後」かが分かります。
modが何もしないときの確認順
- 1
validateでmodの中身を静的に見る
シェルで
claude plugin validateにmodのディレクトリを渡します。イベント名の綴り間違い、manifestの不備、読めないモジュールを、セッションを開かずに拾えます。 - 2
plugin testで環境側の遮断を見る
mod本体のないディレクトリで
claude plugin testを実行します。mod自体が読み込める環境かどうかを、メッセージで判別できます。 - 3
/pluginの「mods active」行を見る
セッション内で
/pluginを開くと、タブの下に1 mod active · first-modのような行が出ます。ここにmodの名前がなければ、読み込みの段階で止まっています。 - 4
debugログに出して名前でgrepする
--debug-fileでログを書き出し、modの名前で絞り込みます。transcriptに何も出ないときの最終手段です。
claude plugin validate ./first-mod
cd ~ && claude plugin test
claude --debug-file ./mod-debug.log --plugin-dir ./first-modclaude plugin testの返すメッセージは、読み込める環境かどうかを次のように分けます。
| メッセージに含まれる語 | 意味 |
|---|---|
no hooks module to load | 意味modは読み込める。このディレクトリにテスト対象がなかっただけ |
hooks modules are turned off here | 意味disableAllHooksか組織のポリシーが遮断している |
the rollout switch served off | 意味Anthropic側でインストール済みmodが遠隔で止められている |
the rollout switch was saved off by an earlier session | 意味以前のセッションが保存した値を使っている。claudeを一度起動して値を更新してから再実行する |
組織がallowManagedModsOnlyを設定している場合、このコマンドは報告しません。その場合は、インストールしたmodが拒否されたとき別のメッセージで理由が出ます。
手元のclaudeで確かめた出力
空のCLAUDE_CONFIG_DIRでclaude --versionとclaude plugin validate --helpを実行すると、v2.1.296では次のとおりでした。ログインやモデル呼び出しは伴いません。
$ claude --version
2.1.296 (Claude Code)
$ claude plugin validate --help
Usage: claude plugin validate [options] <path>
Validate a plugin or marketplace manifest, or the skills, agents, and commands
in a directory
Options:
-h, --help Display help for command
--json Output the validation report as JSON (same exit codes)
--strict Treat warnings as errors (exit 1). Use in CI to fail on
unrecognized fields, missing metadata, and other issues that the
runtime tolerates.--helpの説明文が挙げる検査対象は、manifest、skills、agents、commandsです。troubleshootページが書く「イベント名の綴り間違いや読めないモジュールを拾う」は、この説明文からは読み取れません。CIに組み込むなら--strictを付けると、実行時には許容される未認識フィールドも終了コード1で落とせます。modのhookまで検査対象に含まれるかは、手元のmodに綴り間違いを仕込んで試すのが確実です。
メッセージはどこに出るのか
modを名指しする1行は、セッションの種類によって出る場所が違います。ここを取り違えると「何も出ない」と誤解します。
| セッションの種類 | メッセージの場所 |
|---|---|
--plugin-dirで起動した対話セッション、またはClaudeが書いたmodのホットリロードを有効にしたセッション | メッセージの場所transcriptに薄い色で1行 |
| マーケットプレイスから入れたmodを動かす対話セッションなど、上記以外 | メッセージの場所debugログのみ。claude --debugで起動して確かめる |
--plugin-dir付きのclaude -p | メッセージの場所テキスト出力形式ではstderr。他のmodによる拒否はdebugログのみ |
マーケットプレイス経由のmodが動かないのにtranscriptが静かなのは、不具合ではなく仕様どおりです。最初から--debugを付けて起動し直すのが近道です。
症状1: modが読み込まれない
コマンドも描画も挙動の変化もなく、mods activeの行にも名前がない状態です。debugログには、hooks module、modの名前、not loaded:で始まる行が出ます。たとえば--plugin-dirで読んだmodならhooks module first-mod@inline not loaded: disableAllHooks in managed settingsの形です。コロンの後ろが理由です。
| 理由の書き出し | 原因 |
|---|---|
hooks modules are turned off for installed plugins in this process: the rollout switch served off | 原因Anthropicが遠隔でインストール済みmodを止めている |
同 ...saved off by an earlier session | 原因以前のセッションの保存値を使っている。Claude Codeを起動し直して更新する |
disableAllHooks in managed settings | 原因組織がインストール済みプラグインのhookを止めている |
only managed plugins and built-in plugins run | 原因allowManagedHooksOnlyが有効、または管理設定以外のファイルでdisableAllHooksが有効 |
installed plugins that are not managed load no hooks module in this mode (--bare) | 原因--bareで起動している |
another plugin of that name loads first | 原因同名のプラグインが2つあり、管理されているほう、または先に読まれたほうが使われる |
この表に当たらない場合は、次の原因を上から確かめます。
- バージョンが古い: ターミナルではv2.1.287以降、Desktopアプリではv2.1.286以降が必要です。
claude --versionで確かめます。 validateは通るのにhooksの行が出ない:hooks/hooks.jsonにmodulesキーがないか、綴りが違います。"modules": ["./register.js"]を足します。hooks module did not load:: モジュールの読み込みに失敗しています。トップレベルのコードが例外を投げた場合などで、理由にはファイルと行が出ます。示されたエラーを直します。options do not fit plugin.json userConfig: オプションがuserConfigの検証に失敗しています。maxを超える数値や、必須項目の未入力が典型です。行の末尾にsettings.jsonのpluginConfigsのエントリ名が出るので、そこの値を直します。- 初めて開いたディレクトリで何も読まれない: trustプロンプトに答えていません。そのディレクトリで
claudeを対話起動して受け入れます。 - インストール済みプラグインがすべて読まれない:
--safe-modeで起動していないか確かめます。--safe-modeそのものの使い方はClaude Codeのsafe modeにまとめています。
組織管理の端末やTeam・Enterpriseプランでは、組み込みの保護機構がmodやその応答を拒否することもあります。メッセージにmods are limited to your organization's by policy (allowManagedModsOnly)が含まれていれば、管理者が組織のmodだけを許可しています。debugログのほか、ホットリロード中のセッションではtranscriptにも出ます。
症状2: hookがスキップされる、modが外される
modは読み込まれたのに、特定のhookが飛ばされる、あるいはmodごと外されるケースです。メッセージで原因が分かれます。
| メッセージ | 起きていること | 対処 |
|---|---|---|
hook skipped: | 起きていることhookが例外を投げた、制限時間(1イベントあたり10秒、prompt.editのhookは50ミリ秒)を超えた、または戻り値の形が違った。例: first-mod: tool.call hook skipped: threw Error: boom | 対処全件はdebugログで読む。同じイベント・同じ種類の失敗は、再読み込みまで1回しか表示されない |
no command.run hook answered it | 起きていることmodが登録したコマンドを実行したのに、答えるhookがなかった。例: first-mod registered /tally but no command.run hook answered it | 対処command.runのhookがない、フィルタが別のコマンドを指している、next(e)を返している、のどれかを確かめる |
it crashed the hooks worker | 起きていることfirst-mod was unloaded: it crashed the hooks workerの形で、modが外された | 対処下の説明を参照 |
its session.start ran again in a fresh copy | 起きていることモジュールが再読み込みされ、session.startが新しいコピーで再実行された | 対処下の説明を参照 |
mods that run in the hooks worker are off for this session | 起きていることワーカーが3回止まっても1つのmodに絞れず、組み込み以外のすべてのmod(組織がインストールしたものも含む)を外した | 対処/reload-pluginsで読み込み直す |
no command.run hook answered itには、もうひとつの原因があります。hookがスキップされている場合で、$.ui.openにfocus: falseを渡したときもここに入ります。hookが確かに書いてあるなら、command.runに言及するhook skippedの行を探してください。理由はそこに出ます。テストでコマンドを実行しても同じ理由で失敗するので、再現にも使えます。
インストール済みmodは1つのワーカースレッドを共有しています。ワーカーが応答しなくなったり落ちたりして、原因がこのmodと突き止められると外されます。awaitのないループでスレッドを塞ぐhookが、原因のひとつです。
session.startの再実行は、ワーカーの入れ替えなどで起きます。$.prompt.submit・$.command.run・$.agent.spawnの呼び出しは最初の結果を返すだけで再実行されないため、直すことはありません。v2.1.292より前は、この呼び出しが2回実行され、プロンプトの送信やサブエージェントの起動が重複していました。
症状3: ツール呼び出しが拒否される
modもhookも動いているのに、modが触れたツール呼び出しが拒否されるケースです。拒否の理由として、次の2系統があります。
auto modeの「a hook changed this call's input」
auto modeでは、サーバー側の分類器が審査したあとにhookが呼び出しの入力を書き換えると、審査が実際に実行される内容をカバーしなくなるため拒否されます。書き換えたのはmodのtool.call・turn.stephookでも、settingsのPreToolUsehookでもありえて、メッセージはどれかを示しません。Claudeには「記録されたとおりにもう一度呼び出す」よう伝わります。それでも拒否されるなら、hookが毎回入力を書き換えています。modかhookを止めるか、auto modeを離れて自分で承認します。審査の仕組みはauto modeのサーバー側審査で扱っています。
設定のdenyルールに関するメッセージ
tried to lift a deny rule in your settingsは、modのtool.checkhookが、denyルールで拒否される呼び出しを承認しようとした場合に出ます。呼び出しは拒否されたままです。transcriptとdebugログにmodごと1セッション1回出ますが、claude -pではdebugログのみです。tool.checkで読めるフィールドはmodのtool.check解説にあります。
もう一つのthe deny rules in your settings could not be checked for this call, so it is refusedは、modが承認した呼び出しの検査自体が失敗したため拒否した、というメッセージです。こちらは拒否された呼び出しについてClaudeが読む理由に入ります。
どちらも、modがdenyを越えられない設計の結果です。対処はmodのhookを直すか、必要ならdenyルールの側を管理者と見直すことになります。
症状4: 描画が出ない、反応しない
ペイン、バンド、トースト、キー操作のいずれかが期待どおりに動かないケースです。
- ペインやバンドが空、またはClaude Code標準の表示のまま: hookが返したツリーの検証が通っていません。
--plugin-dirならtranscriptにui.render (Pane) refused:と理由が出ます。debugログにはa hook returned a tree that does not validateが出ます。要素が受け付けないpropや、アプリに存在しない要素が典型です。 threw while drawn:: 返されたツリー、またはnextに渡したpropsを描く途中でエラーになっています。末尾のthe engine drew its ownは、その場所が標準表示にフォールバックしたという意味です。v2.1.289より前は、transcriptの行でこのエラーが起きるとセッションが終了していました。the module failed without a message:Clientがthrow new Error()のようにメッセージのないエラーで失敗しています。Clientのコードでthrowを探してメッセージを付けます。Clientの失敗をhookで受けて描き直す方法はui.faultの解説にあります。$.ui.openを呼んだのにペインが出ない: ユーザー操作に由来しない呼び出しで、しかも端末がそのペインの必要幅より狭いと表示されません。コマンドやボタンから開くか、戻り値のisPlacedを確かめます。- ホットキーが効かない: ペインにキーボードフォーカスがありません。Ctrl+Xに続けてTabを押すか、ペインをクリックします。コマンドから開くときは
focus: trueを付けます。 - ターミナルでは動くのにDesktopアプリで描かれない: そのレンダリング箇所や要素がDesktopでは使えません。リファレンスのrender sitesとelementsの表で対応面を確かめます。
トーストが出ないとき
対話ターミナルで$.ui.toastを呼んでも見えないときは、まずdebugログで呼び出しの行を探します。$.ui.toast (first-mod): build finishedのように、modの名前とトーストの文言が出ているはずです。行がない場合は、$.ui.toast dropped: timeoutMs is a whole number of ms, 1 to 60000のような拒否理由の行を探します。行があるのに見えないときの原因は次のとおりです。
| 状況 | 見分け方と対処 |
|---|---|
| 別のペインがトーストを保留している | 見分け方と対処holdToasts付きで開いたペインが表示中。閉じると解除され、自分のペインなら$.ui.openからholdToastsを外す |
| 従来のレンダラーで、プロンプトの下に出ている | 見分け方と対処右上の箱ではなく、modの名前で始まる1行がプロンプト直下にある |
| 従来のレンダラーで、新しいトーストに置き換えられた | 見分け方と対処古い側のログ行がgave way, cut short(表示中だった)かgave way, unseen(未表示だった)で終わる。1つのトーストにまとめる |
| フルスクリーンレンダリングで描画前に時間切れ | 見分け方と対処同時に描かれるのは3つまで。該当行がleft the stack, never drawnで終わる。まとめて1つにする |
v2.1.290より前は、前回のトーストから2秒以内に出したトーストが捨てられていました。その場合のログはwithin 2000ms of the last; droppedでした。
症状5: 編集や値が消える
modは動いているのに、直したはずの変更や、保存したはずの値が見当たらないケースです。原因は3つに絞れます。
- 編集が反映されない: インストール済みのプラグインを編集しています。Claude Codeが動かすのは、インストール版のキャッシュです。
claude --plugin-dir ./first-modで作業コピーを指して開発すると、保存のたびに再読み込みされます。 - モジュールの再読み込みで値が戻る: モジュールレベルの変数は再読み込みのたびに初期化されます。値は
$.stateか$.storeに置きます。 /clear・/resume・/branchのあとで値が戻る: どのコマンドも$.stateを既定値に戻し、session.startは再発火しません。保存した値の読み込みはclassic.SessionStarthookで行います。
--plugin-dirで編集しているときは、再読み込みのたびにmodの名前とhookの一覧がtranscriptに出ます。保存で壊れた場合はreload failed, the previous version stays loaded:に理由が続き、最後に動いた版が、次に/reload-pluginsなどでプラグインが再読み込みされるまで動き続けます。保存したのに変わらないときは、この行を探します。
debugログの読み方
debugログには、読み込んだ・拒否したモジュール、失敗したhook、拒否した結果の行が1つずつ残ります。transcriptが静かなときの最後の頼りです。
claude --debug-file ./mod-debug.log --plugin-dir ./first-mod
# 別の端末で
tail -f ./mod-debug.log | grep first-mod読み込めたmodには、名前と処理するイベントを並べた行があります。--plugin-dirで読んだmodは名前の後ろに@inlineが付きます。
hooks module first-mod@inline loaded (worker, environment 2, tier user); events: session.start,tool.call,command.run,ui.renderevents:の並びに、期待するhookのイベント名があるかを確かめます。自分の調査用の行を足すには、$.ui.log('message', { to: 'debug' })と第2引数を付けます。第2引数がないと、transcriptに薄い色の行が追加されます。
切り分けを早くする順序
メッセージが見つからないときは、読み込みの前後で2つに割ると速く進みます。mods activeの行に名前があるかが分かれ目です。
- 名前がない: 症状1の表。debugログで
not loaded:を探す - 名前がある:
hook skippedの行、refused:の行、threw while drawn:の行のどれかをdebugログから探す
modを作る側なら、セッションを開く前にclaude plugin validateとclaude plugin testを回せば、イベントの綴りやhookの例外はここで落とせます。modの作り方そのものはmodを80行で作る手順にあります。
まとめ
modが何もしないように見えるのは、失敗がセッションを止めずに1行のメッセージだけで流れるためです。読む場所はセッションの種類で変わり、マーケットプレイスで入れたmodは--debugで起動しないとログすら残りません。まずmods activeの行で読み込みの前後を分け、読み込み前ならrefusalの表、読み込み後ならhook skippedやrefused:の行を、modの名前でgrepして追うのが最短です。