バッチファイルでエラー処理を実装しているつもりでも、実際には意図通りに判定できておらず、エラーを見逃したり、逆に正常処理をエラー扱いしてしまうケースは少なくありません。
原因の多くは文法ミスではなく、ERRORLEVEL の仕様や評価タイミングを前提にした設計になっていないことにあります。
ここでは、実務で特に遭遇しやすい「バッチファイルのエラー処理における失敗例」を、なぜ起きるのかという視点で整理します。
ERRORLEVELを最後にまとめて判定してしまう
複数のコマンドを実行したあとで、まとめて ERRORLEVEL を判定してしまうのは典型的な失敗です。
ERRORLEVEL は直前に実行されたコマンドの結果しか保持しないため、途中で失敗していても、最後のコマンドが成功していればエラーは検知できません。
copy a.txt b.txt
del temp.txt
if %ERRORLEVEL% neq 0 (
echo エラー
)
この例では copy が失敗していても、del が成功すると ERRORLEVEL は 0 になり、エラーが見逃されます。
エラー判定の前に別コマンドを挟んでしまう
ログ出力やデバッグ用の echo を判定前に挟むことで、ERRORLEVEL の参照対象が変わってしまうケースも非常に多く見られます。
echo や set は ERRORLEVEL を更新しないため、過去のエラーコードが残ったままになることもあります。
somecommand
echo 処理完了
if errorlevel 1 (
echo エラー
)
この場合、判定対象は somecommand ではなく、直前の echo になります。
if ERRORLEVELを等号比較だと思い込んでいる
if ERRORLEVEL は「指定値以上かどうか」を判定します。
等号比較だと誤解していると、エラー時でも正常分岐が実行されるように見えます。
somecommand
if errorlevel 1 echo エラー
if errorlevel 0 echo 正常
ERRORLEVEL が 1 の場合でも 0 以上として「正常」が実行されてしまいます。
findやfindstrを単純なエラー判定に使っている
find や findstr は、検索対象が見つからないだけで ERRORLEVEL に 1 を設定します。
処理としては正常終了しているため、「エラーではない 1」を正しく区別しないと、常に失敗しているように見えます。
find "ABC" file.txt > nul
if %ERRORLEVEL% neq 0 (
echo エラー
)
このコードでは「見つからない」だけでエラー扱いになります。
パイプ処理の結果をそのままエラー判定している
パイプを使うと、ERRORLEVEL は最後のコマンドの結果になります。
前段の処理が失敗していても検知できない、あるいは逆に条件不一致を失敗と誤解する原因になります。
type file.txt | find "ABC" > nul
if errorlevel 1 echo エラー
この場合、find の結果だけが評価されます。
括弧ブロック内で%ERRORLEVEL%を参照している
括弧ブロック内では %ERRORLEVEL% がパース時に評価され、実行時の変化を反映しないことがあります。
結果として、どの分岐でも同じ ERRORLEVEL に見える状態になります。
if exist file.txt (
somecommand
if %ERRORLEVEL% neq 0 echo エラー
)
このような構成では、遅延環境変数展開を使わない限り安全に判定できません。
サブルーチンの戻り値を設計していない
call を使って処理を分割している場合、サブルーチン側で exit /b を明示しないと、ERRORLEVEL が不定になります。
その結果、以前のエラーコードが引き継がれることがあります。
call :work
if %ERRORLEVEL% neq 0 echo エラー
exit /b
:work
somecommand
rem exit /b を書いていない
0/非0で一律にエラー扱いしている
robocopy などのコマンドは、0 以外でも成功や警告を意味する場合があります。
仕様を知らずに一律でエラー判定すると、常に失敗しているように見える状態になります。
まとめ
バッチファイルのエラー処理で失敗する多くの原因は、ERRORLEVEL の仕様を前提にせず「感覚的」に判定していることにあります。
エラー判定は、どのコマンドの結果を見ているのか、その終了コードは何を意味するのかを明確にしたうえで、直後に評価する設計が必要です。
失敗例を理解しておくことで、ERRORLEVEL を「信用できない存在」ではなく、「正しく使える判定材料」として扱えるようになります。

