結論
Claude Code(Anthropic のターミナルで動く AI コーディングエージェント)を claude -p(非対話モード)でスクリプトから呼ぶと、ファイルの書き込みが権限の確認や設定で断られても、終了コードは 0 でした。--output-format json の is_error も false、subtype は success です。
「$LASTEXITCODE -eq 0 なら成功」とすると、何も作られていないのに次の処理へ進みます。期待するファイルがあるか と、JSON の permission_denials を合わせて判定するのが安全です。
確認環境: Claude Code 2.1.318、Windows 11 Pro、PowerShell 5.1。確認日 2026-11-06。パスは <TEMP> に置き換えています。
公式ドキュメントの記述
- 非対話モードは「成功で 0、実行が失敗したら 0 以外」で終わる(https://code.claude.com/docs/en/headless 、2026-11-06 確認)
- Manual モード(何も指定しないときの既定)の
-pでは、承認が必要な操作は、何かが承認しない限り拒否される(同ページ) -
-pでは設定ファイルのエラーもダイアログを出さず、壊れた設定を飛ばして続行する(https://code.claude.com/docs/en/settings 、2026-11-06 確認)
権限で断られるのは「実行の失敗」ではなく「依頼の一部ができなかった」扱いなので、終了コードには出ない、という整理になります。
再現手順
空の一時フォルダで、ファイルを1つ作るよう頼みます。私は Claude Code の画面の中から試したため、親セッションの環境変数(CLAUDE で始まるもの)を消した新しい PowerShell で実行しました。
Set-Location <TEMP>\case
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false) # claude の UTF-8 出力を正しく受け取る
$out = claude -p 'このフォルダに hello.txt というファイルを作り、中身を hi の1行にしてください。' --output-format text 2>$null
"exit=$LASTEXITCODE"
"exists=" + (Test-Path .\hello.txt)
$out
結果1: 何も指定しない(Manual モード)
exit=0
exists=False
hello.txt はまだ作れていません。このフォルダへの書き込みを許可していただけなかったので、ファイルは作成されていません。
作るのであれば、書き込みを許可したうえでもう一度お知らせください。中身が `hi` の1行だけの hello.txt を作ります。
返答の文面は実行ごとに少し変わりますが、この条件(モード指定なし)で5回試して、5回とも終了コード 0・ファイルなしでした。
--output-format json にすると、断られた操作が permission_denials に入ります(抜粋)。
{
"type": "result",
"subtype": "success",
"is_error": false,
"num_turns": 2,
"permission_denials": [
{ "tool_name": "Write",
"tool_input": { "file_path": "<TEMP>\\case\\hello.txt", "content": "hi\n" } }
]
}
結果2: 設定で書き込み系のツールを禁止する
.claude\settings.json に deny ルールを書き、--permission-mode acceptEdits(ファイル編集を確認なしで許可するモード)と --output-format json で同じ依頼をしました。下の出力は、JSON の主な項目を Format-List で表示したものです。
{ "permissions": { "deny": ["Write", "Edit", "NotebookEdit", "Bash", "PowerShell"] } }
exit=0
exists=False
type : result
subtype : success
is_error : False
num_turns : 2
result : hello.txt は作れていません。このセッションにはローカルにファイルを書き込めるツール(Write や Bash)が無く、…
このときは permission_denials が空(0件) でした。deny で禁止したツールは最初から使えない扱いになり、「呼んで断られた」記録が残らないためと考えられます。permission_denials だけを見る判定でも見逃します。
なお、deny を ["Write"] だけにして同じこと(acceptEdits)を試すと、Bash と PowerShell の呼び出しが断られたあと、最終的に hello.txt は作られました。「Write を禁止すれば書き込まれない」わけではない点も、合わせて注意が必要です。
対処: 終了コード・JSON・成果物の3つで判定する
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
function Invoke-ClaudeChecked {
param([string]$Prompt, [string]$ExpectedFile)
$raw = & claude -p $Prompt --output-format json 2>$null
$code = $LASTEXITCODE
$r = ($raw -join "`n") | ConvertFrom-Json
$denied = @($r.permission_denials).Count
$exists = Test-Path -LiteralPath $ExpectedFile
[pscustomobject]@{
ExitCode = $code
IsError = $r.is_error
Subtype = $r.subtype
Denials = $denied
Exists = $exists
OK = ($code -eq 0) -and (-not $r.is_error) -and ($denied -eq 0) -and $exists
}
}
$res = Invoke-ClaudeChecked -Prompt 'このフォルダに hello.txt というファイルを作り、中身を hi の1行にしてください。' -ExpectedFile (Join-Path $PWD 'hello.txt')
$res | Format-List
if (-not $res.OK) { Write-Output '失敗として扱います' }
ExitCode : 0
IsError : False
Subtype : success
Denials : 1
Exists : False
OK : False
失敗として扱います
ポイントは次の3つです。
- 終了コードは「起動や API の失敗」を拾うために見る。依頼が達成できたかは分からない
-
permission_denialsは「断られた操作」を拾えるが、deny で最初から外したツールは載らない - 最後は 期待する成果物(ファイル・出力の中身)があるか を確かめる
[Console]::OutputEncoding を UTF-8 にしないと、Windows PowerShell 5.1 では JSON の日本語が化けて ConvertFrom-Json が失敗することがありました。
Claude Code の deny ルールで .env を読ませない設定を試した記録は Zenn にあります: https://zenn.dev/chukidx_dev/articles/claude-code-deny-env-windows