Claude Code の permissions.denyInsteadOfPrompting:夜間セッションは止まらなくなり、拒否された作業はエラーになりません
この新しい設定がなくすのは待ち時間であって、失敗ではありません。拒否された呼び出しはクリーンな終了と success サブタイプで終わるので、設定はセットアップの半分にすぎず、残りの半分は「何が拒否されたか」の記録です。同じリリースでは、フィーチャーフラグを取得しないセッションから auto mode のフォールバックがなくなり、この設定が拒否する対象も増えました。
目次
夜のあいだ放置した Claude Code セッションは、朝まで誰も見ない権限プロンプトで止まることがあります。これを避ける既存の方法には、それぞれ代償があります。bypassPermissions は何も確認しません。dontAsk はプロンプトになるはずの呼び出しを拒否しますが、これは権限モードなので auto mode と置き換わってしまいます。--permission-prompts none は同じことを claude -p の 1 回の実行に対して行います。11月6日にリリースされた Claude Code 2.1.319 では permissions. が追加されました。人の応答を待つことになる呼び出しを、どの種類のセッションでも、どのモードで動かしていてもその下で拒否する設定です。
同じリリースで、この設定が拒否する対象も増えました。テレメトリーやフィーチャーフラグをオフにしていると、サーバーが確認できない auto mode の呼び出しは Claude Code 自身の classifier リクエストにフォールバックしなくなり、ユーザーへの確認に回ります。設定がオンなら、それは拒否を意味します。以下のテストは 11月11日に Linux 上の Claude Code 2.1.322 で、使い捨てリポジトリと Haiku 5.5 を使って実行しました。リリースノートやドキュメントだけを根拠にしている箇所は、そう明記しています。
Claude Code の permissions.denyInsteadOfPrompting は何をするのか
権限プロンプトを拒否に変えます。allow ルール、権限モード、auto mode の classifier が先に判断するのは変わりません。拒否されるのは、本来なら止まって人を待つはずだった呼び出しだけで、Claude には承認の要らない部分の作業を続けるよう伝えられます。設定リファレンスには、対象として -p の実行、ターミナルの対話セッション、VS Code 拡張機能、ローカルの Desktop セッション、サブエージェント、エージェントチームが挙げられています。
ほかに 4 つの詳細が設定リファレンスに書かれています。これらはテストしていません。
- 置ける場所。 ユーザー設定か managed 設定、または
--settingsです。リポジトリの.claude/settings.jsonには置けません。また~/.claude/settings.jsonや--settingsでのtrueは、managed 設定のfalseよりも優先されます。 - モードではなく設定。 auto mode の下で動くので、classifier は定型的な呼び出しを承認し続け、この設定はプロンプトまで落ちてくるものだけを受け止めます。
- ほかに無効になるもの。
AskUserQuestion、EnterPlanMode、ExitPlanModeがツール一覧から外れるので、plan mode のセッションは自力で plan mode を抜けられません(エージェントチームのチームメイトはこの 2 つの plan ツールを保持します)。MCP の elicitation リクエストは、Elicitationフックが応答しない限りキャンセルされます。canUseToolコールバックや--permission-prompt-toolには問い合わせなくなります。課金される classifier リクエストについての通知がダイアログを開く場面では、通知がスキップされ、そのリクエストを必要とするアクションは拒否されます。 - それでも止まる場合。 設定がオンの auto mode では、間に何の作業も挟まずに 20 回連続で拒否されるとターンが終わり("20 actions were refused and no work went ahead in between, so Claude stopped.")、
-pの実行ではこれがerror_during_executionとして報告されます。「作業を続ける」は呼び出し単位の話で、夜間のターンが続くという約束ではありません。
denyInsteadOfPrompting を有効にした Claude Code セッションでも止まることはあるのか
あります。この設定がカバーしない 2 つの場面、つまりワークスペースの信頼ダイアログと、.mcp.json のプロジェクト MCP サーバーの承認です。ドキュメントにもそう書かれていて、最初の対話セッションはまさにそこで止まりました。Claude Code が一度も開いたことのないディレクトリで設定をオンにして起動したところ、"Quick safety check: Is this a project you created or one you trust?" で、こちらが答えるまで止まったままでした。
そのため対話セッションやバックグラウンドセッションでは、セットアップに 1 つ手順が加わります。ジョブが使うディレクトリで一度手動で Claude Code を起動し、信頼ダイアログとプロジェクト MCP サーバーを承認してから終了します。権限のドキュメントによると、信頼はリポジトリのルート単位で記録され、worktree はメインのチェックアウトのルートを使います。そのため信頼済みリポジトリの新しい worktree では再び確認されませんが、別の場所への新しい clone では確認されます。claude -p の実行ではどちらのダイアログも出ません。プロジェクト MCP サーバーには確認なしで接続し、フォルダーが信頼されるまではプロジェクトの allow ルールを単に読み飛ばします。tmux ペインでは信頼ダイアログはさらに厄介で、デフォルトの選択肢が No, exit です。誤って押した Enter が Claude Code のダイアログで何を起こすかは tmux の記事で扱っています。
夜間セッション向けに Claude Code の denyInsteadOfPrompting を設定する方法
この設定には 2.1.319 以降が必要です。2.1.321 では、誰も見ていないときに重要になる権限チェックが 3 つ修正されました(読み取り専用の Bash コマンドがプロンプトなしでサンドボックス外で実行される問題、書き込みが間違ったフォルダーを基準に判定される問題、ディレクトリへの cp や mv が書き込むファイルを deny ルールが見逃す問題)。そのため、このレシピをそれより前のバージョンで動かすことはおすすめしません。ドキュメント自身の例では、設定を開始モードと並べてユーザー設定に置いています。
{
"permissions": {
"defaultMode": "auto",
"denyInsteadOfPrompting": true
}
}このファイルは、自分が画面の前に座っているセッションも含め、起動するすべてのセッションに適用されます。無人ジョブだけに絞るには、起動時に渡します。
claude --permission-mode auto \
--settings '{"permissions":{"denyInsteadOfPrompting":true}}'claude -p の 1 回の実行には、以前からあるフラグ --permission-prompts none(v2.1.259 以降)があります。headless のドキュメントによると、これが一番効くのは、Agent SDK の canUseTool コールバックや --permission-prompt-tool に問い合わせて応答を待つことになる場合です。そうしたホストがない -p の実行は、もともとプロンプトになるはずの呼び出しを拒否します。これは後述の対照実験でも確認できました。対話セッションやバックグラウンドセッションまでカバーするのは、この設定のほうです。
ドキュメントの説明からすると、ほとんど何もプロンプトにならない bypassPermissions では、この設定で変わることはあまりありません。この組み合わせは試していません。この設定が役に立つのは auto mode や、狭い allow ルールの下で、一部の呼び出しがまだプロンプトまで落ちてくる場合です。
denyInsteadOfPrompting を有効にした Claude Code で、拒否された呼び出しはどう見えるか
対話セッションでは、ダイアログはまったく出ません。tmux ペインで manual モードの Claude Code を、--settings で設定を渡して起動し、ファイルを作るよう頼みました。呼び出しには「Not run · permission denied」と表示され、Claude はツール結果として次の文を受け取りました(パスは短縮しています)。
Permission for this tool use was denied. It requires approval, and this session has no approval surface — nobody can answer a permission prompt here — so it was denied automatically. The action was NOT performed; do not claim it succeeded, and do not retry it: this action, and anything else that requires approval, will be denied the same way for the rest of this session. Tell the user what was blocked and why you needed it, then continue with the parts of the task that do not require approval. What required approval: Claude requested permissions to write to /tmp/dipt/hello.txt, but you haven't granted it yet.Claude はこの文の指示に半分だけ従いました。最初はシェルのリダイレクトを試し、それが拒否されると、それでも Write ツールを試し、それも拒否されました。そこでようやく "Not done: I couldn't create hello.txt" と報告し、両方の試行を挙げて、手動で実行する ! コマンドを提案しました。設定をオンにした 2 回の -p 実行のうち 1 回でも、Haiku は同じように別ルートを試しました。2 つ目の対話セッションでは date > now.txt の実行を頼んだところ、最初の拒否で止まり、別の方法は「拒否を回避することになるので」試していないと答えました(原文 "since that would get around the denial")。同じモデル、同じ指示で、振る舞いは 2 通りです。「do not retry」はモデルが読む文章であって、ロックではありません。
claude -p では、この設定の効果は見えにくくなります。権限ホストのない -p の実行は、もともとプロンプトを拒否するからです。同じタスクを設定ありとなしで実行しました。どちらも exit 0 で終わり、"subtype": "success" と "is_error": false を返し、どちらも拒否された呼び出しを permission_denials に列挙し、stream-json に permission_denied メッセージを出力しました。違ったのはメッセージの中身です。設定なしでは、Claude が受け取ったのは "Claude requested permissions to write to … but you haven't granted it yet" だけで、permission_denied イベントに理由はありませんでした。設定ありでは、Claude は上の「do not retry」の全文を受け取り、イベントには理由が付いていました。
"decision_reason": "no approval surface in this session; permission request denied automatically"denyInsteadOfPrompting の下で Claude Code が飛ばした作業を見つける方法
終了コードでは見つかりません。-p の実行では、拒否された呼び出しはすべて exit 0 と success サブタイプで終わり、対話セッションは単にアイドルに戻りました。Haiku の要約は毎回正直でしたが、要約はモデルが自分の作業について語ったものにすぎず、夜間ジョブが「誰かが文章を読むこと」に依存すべきではありません。機械的なチェックが 2 つ使えます。
-p の実行では、Claude 自身の終了コードを保持したうえで、拒否された呼び出しを含め、ほかにおかしな点があれば失敗にします。
claude -p "$TASK" --permission-mode auto --output-format json \
--settings '{"permissions":{"denyInsteadOfPrompting":true}}' > run.json
rc=$?
python3 - "$rc" <<'EOF' || exit 1
import json, sys
rc = int(sys.argv[1])
try:
d = json.load(open("run.json"))
except Exception:
sys.exit("no readable result")
n = len(d.get("permission_denials", []))
print(f"exit={rc} subtype={d.get('subtype')} is_error={d.get('is_error')} refused={n}")
sys.exit(1 if rc or d.get("is_error") or d.get("subtype") != "success" or n else 0)
EOFこれでわかるのは、実行中に拒否もエラーもなかったことです。タスクが完了したかどうかはわかりません。そのチェックはタスク側の仕事です。
対話セッションやバックグラウンドセッションには結果オブジェクトがないので、リクエストが起きた時点で記録します。すぐ思いつくフックの PermissionDenied は、ここでは発火しません。フックリファレンスによると、これが動くのは auto mode が呼び出しを拒否したときだけで、deny ルールや、誰も答えられないプロンプトでは動きません。PermissionRequest は Claude Code がプロンプトを表示する前に動くので、設定が拒否する前に呼び出しを見られます。リクエストを書き留めるだけで判断を返さないフックなら、拒否はそのまま残ります。~/. として保存し、chmod +x してください。
#!/usr/bin/env python3
# PermissionRequest hook: record the call, decide nothing.
import json, os, sys, datetime
e = json.load(sys.stdin)
row = {
"t": datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds"),
"session": e.get("session_id"),
"tool": e.get("tool_name"),
"input": e.get("tool_input"),
}
with open(os.path.expanduser("~/.claude/prompt-requests.log"), "a") as f:
f.write(json.dumps(row) + "\n")設定と同じ --settings ファイルで読み込みます。こうすれば無人ジョブでだけ動き、日中に手で承認するプロンプトでは動きません。
{
"permissions": { "denyInsteadOfPrompting": true },
"hooks": {
"PermissionRequest": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "~/.claude/hooks/log-request.py" }] }
]
}
}この方法で -p の実行と tmux の対話セッションの両方に読み込みました。前者では拒否された Write を、後者では拒否された Bash 呼び出しを記録し、どちらの呼び出しも拒否されたままでした。2 行目は次のとおりです(セッション ID は短縮しています)。
{"t": "2026-11-11T02:25:43+00:00", "session": "7a533308-…", "tool": "Bash", "input": {"command": "date > now.txt", "description": "Write current date to now.txt"}}このログには限界が 3 つあります。ログの各行は、プロンプトになるはずだったリクエストです。それが拒否を意味するのは、設定がオンで、ほかの PermissionRequest フックがその呼び出しを承認しない場合に限られます。承認するフックも動かしているなら、行の意味はそのフック次第です。次に、ファイルはセッション間で共有されるので、時刻ではなく session で絞り込んでください。そして、フックが動いていなければ空のログは何も証明しません。空のファイルを信用する前に、サンプルのイベントを一度スクリプトにパイプで流し、確実に拒否される呼び出しを 1 回行ってください。設定リファレンスによると、一部の呼び出しはどのフックにも問い合わせる前に拒否されます(どの ask ルールにも該当しない、auto mode でのクリティカルパスの削除)。そのため、このログは下限であって完全な一覧ではありません。auto mode による拒否の分は、PermissionDenied フックで拾えます。フックが関門より誘導に向いている理由は フックの記事で扱っています。
テレメトリーやフィーチャーフラグをオフにした Claude Code の auto mode で何が変わったか
セッション単位のフォールバックがなくなりました。権限モードのドキュメントによると、2.1.282 以降、フィーチャーフラグを取得しないセッションは、デフォルトで auto mode のアクションのレビューをサーバーに依頼します。サーバーがそのセッションをまったくレビューしない場合(レビュー用のフィールドを落とすゲートウェイ、サーバー側のチェックがないプラットフォームや認証情報)、Claude Code は以前はセッションの残りの間、自身の classifier リクエストにフォールバックしていました。2.1.319 では、"with telemetry or feature flags off, including behind LLM gateways" のセッションについてこれが変更されました(リリースノートより)。サーバーが確認できない呼び出しは "are asked about or denied, with no fallback to the built-in classifier"、つまりユーザーに確認されるか拒否され、組み込みの classifier にはフォールバックしません。denyInsteadOfPrompting がオンなら、「確認される」は「拒否される」になります。なお、レビューするサーバーから判定が返ってこなかった単発のアクションは、このリリース以前から拒否されていました。この部分は新しくありません。
影響を受けるかどうかは、テレメトリーのスイッチよりも接続経路で決まります。Anthropic API に直接接続した自分のセッションでは、テレメトリーをオフにしてもサーバーレビューはオフになりませんでした。DISABLE_TELEMETRY=1 の状態で auto mode の /status は Auto mode server: Enabled と表示し、auto mode の -p 実行はシェルコマンドを拒否なしで実行しました。この変更が想定しているのは、レビューのリクエストやその結果を落とす LLM ゲートウェイやプロキシです。そうしたセッションは以前は Claude Code 自身の classifier リクエストを使っていましたが、今はプロンプトか拒否になります。ゲートウェイ越しのテストは一度もしていないので、この部分はリリースノートが根拠です。ゲートウェイが手を加えずに通す必要があるものは ゲートウェイの記事で扱っています。同じノートによると、Bedrock、Vertex、Foundry は変わりません。
このノートには読み違えやすい行が 2 つあります。同じく 2.1.319 で追加された CLAUDE_CODE_DISABLE_FEATURE_FLAGS は、テレメトリーをオンのままフラグの取得を止めますが、フォールバックは戻りません。ノートは "telemetry or feature flags off" をひとまとめに挙げているので、DISABLE_TELEMETRY から切り替えて変わるのは送信する内容であって、auto mode の振る舞いではありません。一方 CLAUDE_CODE_AUTO_MODE_SERVER= はフォールバックを戻します。サーバーにまったく問い合わせないからです。ドキュメントによると、Enterprise プランと、API、Bedrock、Vertex、Foundry のアカウントでは、こうした classifier リクエストはトークン使用量に計上されます。サーバーレビューではその分は増えません。
11月11日に読んだ時点の権限モードのページは、2.1.319 の例外を書かずにセッション単位のフォールバックを説明したままです。テレメトリーやフラグがオフのセッションについては、ドキュメントが追いつくまでリリースノートに従ってください。
Claude Code の auto mode がどちらの経路を使っているか確認する方法
/status を実行し、Auto mode server の行を読みます。自分のセッションでは、DISABLE_TELEMETRY=1 だけなら Enabled、CLAUDE_CODE_AUTO_MODE_SERVER= を加えると Disabled と表示されました。2.1.319 では、レビューがセッションに届いていないのにこの行が Enabled と表示される問題(たとえばレビューを落とすゲートウェイの背後)も修正されています。それより前のバージョンでは、ここの Enabled は何の証明にもなりませんでした。サーバーがレビューしていないとわかったら、Claude Code 自身の classifier がどのモデルで動いているかの確認方法を auto mode セットアップの記事で紹介しています。
同じリリースでは -p の終了コードも修正されました。安全チェックが使えないために auto mode がアクションを拒否した場合、claude -p は以前は exit 0 を返していましたが、今は text 出力でも JSON 出力でも exit 1 を返します。この修正の対象はチェックのない auto mode であって、拒否全般ではありません。default モードでの拒否は相変わらず exit 0 で終わったので、上のスクリプトは permission_denials も数えています。フックリファレンスによると、PermissionDenied フックは判定なしの拒否を "Auto mode could not evaluate this action and is blocking it for safety" で始まる reason 付きで受け取ります。これで、チェックが欠けていた場合と、classifier が呼び出しを見たうえで拒否した場合を区別できます。
Claude Code の denyInsteadOfPrompting はオンにすべきか
誰も見ていない対話セッションやバックグラウンドセッションなら、オンにすべきです。ただしログと組み合わせる場合に限ります。この場合、設定は見える失敗の代わりに、調べないとわからない失敗を残します。止まったセッションは朝になれば一目でわかります。ペインがダイアログで止まり、ほかに何も起きていないからです。設定をオンにしたセッションはターンを終え、拒否された呼び出しは要約に、-p の実行なら permission_denials に、フックを設定していればそのログに残ります。自動で失敗として表に出るものはありません。claude -p では、この設定で変わることは少なめです。こうした実行はもともと拒否していたからです。加わるのは「do not retry」の文だけで、自分の実行のうち 1 回はそれを無視しました。ドキュメントには、Claude が承認の要らないステップを続けると書かれています。自分のタスクはどれも 1 ステップだったので、示せたのは拒否だけです。
auto mode の変更も同じ方向に働きます。サーバーレビューを落とすゲートウェイの背後では、2.1.319 で静かなフォールバックがプロンプトに変わり、設定がオンならさらに多くの拒否に変わります。ログがないなら、止まったセッションのほうが正直な失敗です。
01 / ディスカッション
コメント欄はありません。議論は X で行っています。
02 / ほかの記事
Claude Code のスラッシュコマンド: 知っておきたい組み込みコマンドと、自作の /command がいまはスキルである理由
commands フォルダはマクロではなく、Claude が呼び出せるコードとして扱ってください。どのファイルがモデルによる呼び出しを有効のままにしているかを監査し、スクリプト実行では終了コードを信用せずにターン数を確認し、副作用のある処理の保証は呼び出しフラグではなく権限とフックに置いてください。
Claude Code と Gemini CLI の分業:開発者による 7 つの使い分けと、6 月 18 日を越えて残ったもの
2 つのツールを組み合わせる 2025 年のセットアップは、無料の Google ログインと、より大きなコンテキストウィンドウに頼っていました。そのログインは 6 月に別のツールへ移り、ウィンドウでは Claude Code が追いつきました。どのセットアップが残ったかを決めたのは、ツールそのものではなく、各呼び出しの背後にあるアカウントでした。
Claude Code のバックグラウンドサブエージェントと利用上限:ステータスラインが99%安くなるという Anthropic の主張と、自分で測れるキャッシュ書き込み
サブエージェントの要は分離にあり、自分のトランスクリプトでは、そのコストの大半が5分のキャッシュ書き込みでした。Max プランで計測すると、バックグラウンドサブエージェントは価格換算した使用量の約11.5%で、その64%がキャッシュ書き込みです。再現用の Python スクリプトと、4つのレバーがそれぞれコストのどの部分に効くかをまとめます。