みなさんこんにちは!ヒロポンです!
PowerShell で try-catch を書いたのに、 エラーが catch されずに後続コードが普通に実行された ことはないですか??
try {
Get-ChildItem -Path '/notexist'
Write-Host "ここが何故か実行される"
} catch {
Write-Host "なぜここに来ない?"
}
俺もこれで 30分ハマって「なんで catch 入らないの…??」 とコードを睨み続けた 記憶があります。 PowerShell 初心者あるあるで、 C# の例外処理感覚で書くと普通に詰まる場所。
正体は Non-terminating エラー という PowerShell 固有の概念。 で、 これを制御するのが ErrorAction という cmdlet パラメータです。
今回は ErrorAction の3パターン (Continue / Stop / SilentlyContinue) の挙動を、 Docker で実走したターミナル出力つきで解説します。
TL;DR
- PowerShell のエラーは Terminating と Non-terminating の2種類で、 多くの cmdlet エラーは Non-terminating
try-catchが効くのは Terminating だけ。-ErrorAction Stopを付けて Non-terminating を Terminating に変換するのが正解- 業務SE が押さえる ErrorAction は 3つ: Continue (デフォルト・catch効かない) / Stop (catch効く) / SilentlyContinue (エラー抑制)
- ハマりポイントは3つ:
$ErrorActionPreference全域設定の副作用 / SilentlyContinue 乱用 / catch 内で$_を見落とす - Docker (verify-pwsh container) 検証は PowerShell 言語仕様レベルのみ。 Active Directory や Exchange など モジュール固有 cmdlet は Windows 実機環境 で別途確認してください
原因 — Non-terminating エラーの正体
C# / Java 出身者が PowerShell でハマる最大の落とし穴がこれ。
PowerShell のエラーには 2種類 あります。
| 種類 | 例 | try-catch | 挙動 |
|---|---|---|---|
| Terminating | 構文エラー / throw / .NET 例外 |
◎ catch される | 実行が即停止 |
| Non-terminating | Get-ChildItem の Not Found / Write-Error |
× catch されない | エラー出力後に続行 |
問題なのは、 業務系で使う cmdlet の大半は Non-terminating エラーを返す こと。 ファイルが無い、 ユーザーが無い、 ネットワーク到達不可、 全部 Non-terminating。
つまり「try-catch で安心」 と書いたつもりが、 エラー出力が赤字で出るだけで後続コードがそのまま実行される。 これが業務系で一番怖い挙動です。
ハンズオン #1 — Continue (デフォルト) で catch されない
実際にやってみます。 Write-Error で意図的に Non-terminating エラーを出して、 try-catch がどう動くか確認。
Write-Host "=== Block 1: ErrorAction Continue (デフォルト) ==="
Write-Host "[1] try ブロックの直前"
try {
Write-Error "ファイルが見つかりません: /notexist-12345"
Write-Host "[2] Write-Error の直後 — Continue だとここが実行される"
} catch {
Write-Host "[CATCH] 捕捉メッセージ: $($_.Exception.Message)"
}
Write-Host "[3] スクリプト末尾"
verify-pwsh container で実走した結果がこちら。

注目ポイントは3つ。
[2]が 実行されている → エラーが出ても後続コードが走った[CATCH]が 出ていない → try-catch が機能していない- 赤字で
Write-Error: ファイルが見つかりません→ エラー自体は stderr に出力されている
これが Non-terminating エラーの典型挙動。 「エラーは出るけど例外じゃない」 という、 C# / Java 出身者の直感を裏切るやつです。
ちなみに Get-ChildItem -Path '/notexist' でも同じ挙動になります (cmdlet 内部で Write-Error を呼んでいるため)。 業務系で多用する Invoke-WebRequest Get-ADUser Test-Connection も同類。
ハンズオン #2 — Stop を付けると catch が効く
同じコードに -ErrorAction Stop を1つ追加するだけで挙動が変わります。
Write-Host "=== Block 2: ErrorAction Stop ==="
Write-Host "[1] try ブロックの直前"
try {
Write-Error "ファイルが見つかりません: /notexist-12345" -ErrorAction Stop
Write-Host "[2] この行は実行されない (Stop で例外発生)"
} catch {
Write-Host "[CATCH] 捕捉メッセージ: $($_.Exception.Message)"
}
Write-Host "[3] スクリプト末尾"
実行結果。

今度は3点ぜんぶ逆転。
[2]が 出ていない → try ブロックは Write-Error の時点で中断[CATCH]で メッセージが捕捉 された → catch ブロックが実行された- stderr が 空 → catch で握ったので error stream に流れていない
これが正解の挙動。 -ErrorAction Stop で Non-terminating を Terminating に変換 することで、 ようやく try-catch が いい感じに機能します。
3 ErrorAction の比較表 — 3軸で違いを並べる
ErrorAction には全部で6種類ありますが、 業務SE が押さえるのは 3パターン で十分。 try-catch 連携 / 再実行可能性 / ログ残し方の3軸でまとめたのが下の表です。

要点:
- Stop — try-catch 効く・catch 内で構造化ログ出せる・冪等性確保 → 業務系の止めるべき処理に最適
- Continue (デフォルト) — try-catch 効かない・赤字エラーは出る → 通知だけで済む処理向け
- SilentlyContinue — エラー痕跡ゼロ・失敗に気付けない → 想定済みエラーの抑制のみ
Ignore / Inquire / Suspend もありますが、 業務SE が日常使いするのはこの3つだけです。
ハマりポイント3つ (現場で踏みがちなやつ)
ここまで「ErrorAction Stop を付ければ大丈夫」 みたいに書いてきましたが、 そうじゃないケースが3つあります。
① $ErrorActionPreference = 'Stop' の副作用
スクリプト全体に Stop を効かせるなら、 先頭にこう書けば一発です。
$ErrorActionPreference = 'Stop'
ただしこれ、 想定外の cmdlet まで Stop になる。 俺もある日、 ログイン履歴チェック用のスクリプトに $ErrorActionPreference = 'Stop' を仕込んだら、 Test-Connection のタイムアウトでスクリプト全体が落ちて、 朝1時間原因に辿り着けなかった経験があります。
対策は try-catch で囲んだ範囲だけ Stop にする か、 cmdlet 単位で -ErrorAction Stop を付ける。 グローバル設定は使う場面を絞るのが安全。
② SilentlyContinue 乱用の弊害
「エラーが赤字で出るのが鬱陶しい」 という理由で -ErrorAction SilentlyContinue を多用すると、 本当に止めるべきエラーまで握り潰す ことになります。
俺もこれで、 ある夜間バッチで Copy-Item -ErrorAction SilentlyContinue を使ってたら、 コピー先のディスクが full で 3日分のファイルが消失 していたのを翌朝まで把握できなかった経験があります。 朝出社して気付いて肝が冷えました。
対策は SilentlyContinue を 想定済みのエラー (例: ファイル存在チェックで無くてもOK な場面) のみ に絞る。 デフォルトは Continue (赤字で出るほう) のままにしておく。
③ catch 内で $_ を見落とす
catch ブロックは入ってる前提だけど、 どんなエラーかをログに残さない と後でデバッグできない。
# ✗ 中身ゼロ
} catch {
Write-Host "エラーが発生しました"
}
# ✓ $_ でエラー情報を取り出す
} catch {
Write-Host "エラー: $($_.Exception.Message)"
Write-Host "発生位置: $($_.InvocationInfo.PositionMessage)"
Write-Host "スタックトレース: $($_.ScriptStackTrace)"
}
これは去年、 後輩が書いたスクリプトのデバッグを2時間やった時に「$_ 見てないせいで原因が一切分からん」 という事態に踏み込んだ経験から。 catch 句は $_ の中身をログに残す のが鉄則。
まとめ
PowerShell のエラー処理は C# / Java の直感が通用しない 最大の落とし穴領域。 ポイントを3つだけ:
try-catchを書く時は-ErrorAction Stopもセット で書く$ErrorActionPreference = 'Stop'はスクリプト先頭で全体適用できるが副作用に注意SilentlyContinueは想定済みエラーのみ に絞り込む・乱用しない
これで PowerShell エラー処理の地雷踏みは いい感じに 9割回避できます!!
ぶっちゃけ、 業務SE が PowerShell に手を出した時の 「なんで try-catch 効かないんだ??」 は こんな感じで全員一度は通る道です!! ここを早期に踏んでおくと、 後輩が同じところで悩んでる時に 「ああそれ ErrorAction Stop 付けてないだろ」 と即座に救えるようになるので、 結構美味しいスキルです。
よくある質問
Q1. try-catch を書いたのに catch ブロックが実行されないのはなぜですか?
A. PowerShell の多くの cmdlet が出すのは Non-terminating エラー で、 デフォルトの ErrorAction Continue では try-catch では捕捉できません。 -ErrorAction Stop を付けると Terminating に変換されて catch が効きます。 これが PowerShell 初心者あるある№1です。
Q2. ErrorAction を全体に効かせるには?
A. スクリプト先頭に $ErrorActionPreference = 'Stop' を書くと、 その配下の cmdlet 全てに ErrorAction Stop が適用されます。 ただしハマりポイント①で書いた通り、 想定外の cmdlet までStop になる副作用 があるので、 使う場面は絞るのが安全。
Q3. SilentlyContinue はどんな時に使う?
A. 想定済みの軽微なエラー (例: ファイル存在チェックで無くてもOK・テンポラリ削除で既に消えてた場面) を抑制したい時だけ。 乱用すると重大なエラーまで無視して、 デバッグ困難になります。 デフォルトは Continue (赤字で出る方) のままにしておくのが推奨です。
関連記事
- C# 例外処理の正解 — try-catch-finally / using / Exception フィルタの使い分け — C# 側の例外処理パターン (PowerShell との対比軸)
- C# DataAdapter.Update() で DBNull 例外が出た時の最短対処 — DB 系の Non-terminating っぽいエラーの対処例
以上!

コメント