「On Error Resume Next を書いておけばエラーが出ても止まらないから安心」と思って使い始めたら、どこでエラーが起きているのかまったくわからなくなった――これはVBAを使い始めた人が必ずといっていいほど経験するトラブルです。On Error Resume Next はエラーをなかったことにするのではなく、エラー情報を Err オブジェクトに保持しながらコードを続行するという動作をします。使い方を誤ると、処理が壊れたままサイレントに進んでいく「最悪のデバッグ地獄」に陥ります。本記事では、On Error Resume Next の正しい仕組みから、エラー箇所の特定方法、適切な使い方、そしてプロジェクト全体を守るエラーハンドリング設計まで、コード付きで徹底解説します。
目次
- On Error Resume Nextとは何か――誤解されやすい動作の仕組み
- どこでエラーが出ているかを特定する方法
- Errオブジェクトの正しい使い方
- やってはいけないOn Error Resume Nextの使い方
- 正しいOn Error Resume Nextの使い方
- On Error GoToによる本格的なエラーハンドリング
- エラーログを自動記録する仕組みを作る
- On Error Resume Next地獄から脱出するデバッグ手順
- プロジェクト全体のエラーハンドリング設計
- On Error Resume Next チェックリスト
- まとめ
On Error Resume Nextとは何か――誤解されやすい動作の仕組み
On Error Resume Next は、「エラーが発生したときにプログラムを止めず、エラーが起きた行の次の行から処理を続行する」という命令です。エラーは消えるのではなく、Err オブジェクトに保存されます。コードを続行しながら、後から Err.Number でエラーが起きたかどうかを確認できる設計になっています。
' On Error Resume Nextの動作を確認するコード
Sub UnderstandOERN()
On Error Resume Next
Dim x As Long
x = 1 / 0 ' ゼロ除算エラー(エラー11)が発生
' エラーが起きても次の行に進む
Debug.Print "x = " & x ' x = 0(エラーで代入されなかったまま)
' Errオブジェクトにエラー情報が残っている
Debug.Print "Err.Number = " & Err.Number ' → 11
Debug.Print "Err.Description = " & Err.Description ' → 0 で除算しました
On Error GoTo 0 ' エラー処理を元に戻す
End Sub
ここで重要なのは、On Error Resume Next はエラーを握りつぶすのではなく、「エラーが起きても止まらない状態にする」命令だということです。エラー情報は Err オブジェクトに残るため、直後に Err.Number を確認すれば「エラーが起きたかどうか」を判断できます。
問題が起きるのは、On Error Resume Next を書いたまま Err.Number の確認をせず、さらに Err.Clear もしないで処理を続けるという使い方です。これをやると複数のエラーが積み重なり、どこで何が起きたか追跡不能になります。
どこでエラーが出ているかを特定する方法
「On Error Resume Next を外すとどこかでエラーが止まる」「でも外すと業務が動かない」という状況で、エラー箇所を特定する方法を解説します。
方法1:On Error Resume Nextを一時的に外してF8ステップ実行する
最もシンプルで確実な方法です。On Error Resume Next の行をコメントアウトしてから、VBAエディタでF8キーを押してステップ実行します。エラーが起きた行で処理が止まり、黄色くハイライトされます。
' デバッグ時:On Error Resume Nextをコメントアウトして原因を特定
Sub FindErrorStep()
' On Error Resume Next ' ← 一時的にコメントアウトしてステップ実行
Dim ws As Worksheet
Dim rng As Range
Set ws = ThisWorkbook.Sheets("データ") ' ← ここでエラーが止まればシート名が原因
Set rng = ws.Range("A1:A100") ' ← ここで止まればRange指定が原因
Dim val As Long
val = rng.Cells(1, 1).Value ' ← ここで止まれば値の型が原因
Debug.Print val
End Sub
方法2:各行の後にErr.Numberを確認するデバッグコードを挿入する
ステップ実行が難しい長いコードや、ループ処理の中でエラーが起きている場合は、疑わしい範囲の各行の後に Err.Number チェックを挟んで原因を絞り込みます。
' ========================================
' 各行でErr.Numberを確認するデバッグパターン
' ========================================
Sub DebugWithErrCheck()
On Error Resume Next
' --- ステップ1 ---
Err.Clear
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("データ")
If Err.Number <> 0 Then
Debug.Print "ステップ1でエラー: " & Err.Number & " / " & Err.Description
Err.Clear
' 必要なら処理を止める
' On Error GoTo 0
' Exit Sub
End If
' --- ステップ2 ---
Err.Clear
Dim rng As Range
Set rng = ws.Range("A1:A100")
If Err.Number <> 0 Then
Debug.Print "ステップ2でエラー: " & Err.Number & " / " & Err.Description
Err.Clear
End If
' --- ステップ3 ---
Err.Clear
Dim val As Long
val = rng.Cells(1, 1).Value
If Err.Number <> 0 Then
Debug.Print "ステップ3でエラー: " & Err.Number & " / " & Err.Description
Err.Clear
End If
On Error GoTo 0
Debug.Print "処理完了: val = " & val
End Sub
方法3:行番号付きのエラートレースを実装する
' ========================================
' 行番号トレース付きのデバッグコード
' 長いプロシージャのどのブロックでエラーが起きているかを特定する
' ========================================
Sub TraceErrorLocation()
On Error Resume Next
Dim tracePoint As String ' 現在の処理ポイントを記録する変数
' トレースポイントを設定しながら処理を進める
tracePoint = "1: シート取得"
Err.Clear
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("集計")
If Err.Number <> 0 Then GoTo TraceError
tracePoint = "2: 最終行取得"
Err.Clear
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
If Err.Number <> 0 Then GoTo TraceError
tracePoint = "3: データ読み込み"
Err.Clear
Dim data As Variant
data = ws.Range("A2:C" & lastRow).Value
If Err.Number <> 0 Then GoTo TraceError
tracePoint = "4: 計算処理"
Err.Clear
Dim i As Long
For i = 1 To UBound(data, 1)
ws.Cells(i + 1, 4).Value = CDbl(data(i, 2)) * CDbl(data(i, 3))
If Err.Number <> 0 Then
tracePoint = "4: 計算処理(" & i & "行目)"
GoTo TraceError
End If
Next i
On Error GoTo 0
MsgBox "処理完了", vbInformation
Exit Sub
TraceError:
Dim errNum As Long
Dim errDesc As String
errNum = Err.Number
errDesc = Err.Description
Err.Clear
On Error GoTo 0
MsgBox "エラー発生箇所: " & tracePoint & vbCrLf & vbCrLf & _
"エラー番号: " & errNum & vbCrLf & _
"内容: " & errDesc, vbCritical, "エラートレース"
Set ws = Nothing
End Sub
方法4:イミディエイトウィンドウでErr.Numberを逐次確認する
' VBAエディタで Ctrl+G を押してイミディエイトウィンドウを開き、
' ステップ実行中(F8)に以下を入力してエラー状態を確認する
' ? Err.Number → 現在のエラー番号(0ならエラーなし)
' ? Err.Description → 現在のエラーの説明文
' ? Err.Source → エラー発生元の情報
' ステップ実行とイミディエイトウィンドウを組み合わせると
' どの変数がどの状態のときにエラーが起きているかを
' リアルタイムで追跡できる
Errオブジェクトの正しい使い方
On Error Resume Next を正しく使うために、Err オブジェクトのプロパティと動作を正確に理解しておく必要があります。
Errオブジェクトのプロパティ
Err.Number:エラー番号。エラーが起きていない場合は0。On Error Resume Nextの後でこれを確認してエラーの有無を判断するErr.Description:エラーの説明文(日本語)。デバッグ時のログ記録に使うErr.Source:エラーが発生したオブジェクトまたはアプリケーションの名前Err.Clear:Err.Numberを0にリセットする。次のエラーチェックを正確に行うために、チェックの直前に必ず呼ぶ
Err.Clearが必須な理由
' ========================================
' Err.Clearを忘れると前のエラーが残り続ける
' ========================================
Sub ErrClearImportance()
On Error Resume Next
' 1回目のエラーを起こす
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("存在しないシート") ' エラー9発生
Debug.Print "1回目のErr.Number: " & Err.Number ' → 9
' Err.Clearせずに次の操作へ
Dim x As Long
x = 100 ' エラーは起きないはず
Debug.Print "2回目のErr.Number: " & Err.Number ' → 9 のまま!(クリアされていない)
' → x = 100の代入は成功しているのに、まだエラー9が残っている
' これが「どこでエラーが出ているか不明」の大きな原因
' ---- 正しい書き方 ----
Err.Clear ' チェックの前に必ずクリア
Dim y As Long
y = 200 ' エラーなし
If Err.Number <> 0 Then
Debug.Print "y の代入でエラー: " & Err.Number
Else
Debug.Print "y の代入は正常: y = " & y ' → ここが表示される
End If
On Error GoTo 0
End Sub
Errオブジェクトが自動クリアされるタイミング
Err.Number は以下のタイミングで自動的に0にリセットされます。これを知らないと「さっきエラーが出ていたのにErr.Numberが0になっている」という混乱が起きます。
Err.Clearを呼んだときResume・Resume Next・Resume ラベルを実行したときOn Error GoTo 0を実行したとき- プロシージャを終了したとき
- 新しいエラーが発生したとき(古いエラーは上書きされる)
' ========================================
' Errオブジェクトの自動クリアを意識したコード
' ========================================
Sub ErrAutoClearDemo()
On Error Resume Next
' エラーを起こす
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("不在シート")
' ここでErr.Numberは9
Debug.Print "エラー直後: " & Err.Number ' → 9
' 別のエラーを起こすと上書きされる
Dim x As Long
x = 1 / 0 ' エラー11(ゼロ除算)
Debug.Print "上書き後: " & Err.Number ' → 11(9は消えた)
' On Error GoTo 0でクリアされる
On Error GoTo 0
' ここでErr.Numberは0
Debug.Print "GoTo 0後: " & Err.Number ' → 0
End Sub
やってはいけないOn Error Resume Nextの使い方
以下のパターンは「どこでエラーが起きているかわからない」状態を生む典型的なアンチパターンです。既存コードにこれらが含まれていないか確認してください。
アンチパターン1:プロシージャ全体をOn Error Resume Nextで包む
' NG:プロシージャ全体をOn Error Resume Nextで包んでいる
' エラーがどこで起きても処理が続行されるため、
' 誤った結果が出力されてもエラーに気づけない
Sub BadPattern1()
On Error Resume Next ' ← プロシージャの先頭でオン
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
Set ws = ThisWorkbook.Sheets("データ") ' シートがなくてもスルー
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row ' wsがNothingでも続行
' → lastRowは0になりループが回らない、あるいはエラーが起きたまま続行
For i = 1 To lastRow
ws.Cells(i, 3).Value = ws.Cells(i, 1).Value + ws.Cells(i, 2).Value
' 型エラーが起きてもスルー → 間違った値が書き込まれる
Next i
' On Error GoTo 0 もない → エラー状態のまま次の処理へ
End Sub
アンチパターン2:Err.Clearなしに複数の操作をまとめてOn Error Resume Nextで囲む
' NG:複数操作をまとめてOn Error Resume Nextで囲み、最後に一度だけチェック
' どの行でエラーが起きたかわからない
Sub BadPattern2()
On Error Resume Next
Dim ws As Worksheet
Dim rng As Range
Dim val As Long
Set ws = ThisWorkbook.Sheets("集計")
Set rng = ws.Range("A1:C100")
val = rng.Cells(1, 1).Value
' 最後に一度だけチェック → どこでエラーが起きたか不明
If Err.Number <> 0 Then
Debug.Print "どこかでエラーが出ました: " & Err.Number
End If
On Error GoTo 0
End Sub
アンチパターン3:On Error GoTo 0で解除するのを忘れる
' NG:On Error Resume Nextを解除せずにプロシージャを終了する
' 次に呼び出したプロシージャにも影響が残る場合がある
Sub BadPattern3()
On Error Resume Next
' 何らかの処理
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)
' On Error GoTo 0 がない → エラー状態が次の処理に引き継がれる可能性
' プロシージャ終了(エラーハンドリングが有効なまま)
End Sub
Sub CalledAfterBadPattern3()
' BadPattern3の後で呼ばれるプロシージャ
' On Error Resume Nextの効果はプロシージャを跨がないが、
' Err.Numberが0でない状態が残ることがある
' → 混乱の原因になる
End Sub
正しいOn Error Resume Nextの使い方
On Error Resume Next が本来適切に使われるのは、「エラーになることが想定されるが、失敗しても処理を続けたい1〜2行の操作」に対して、直前にオンにして直後にオフにするという局所的な使い方です。
正しい使用パターン:オブジェクトの存在確認
' ========================================
' 正しい使い方1:オブジェクトの存在確認(最も一般的な用途)
' ========================================
' シートの存在確認
Function SheetExists(sheetName As String, Optional wb As Workbook) As Boolean
If wb Is Nothing Then Set wb = ThisWorkbook
Dim ws As Worksheet
' On Error Resume Nextをこの1行だけに限定する
On Error Resume Next
Set ws = wb.Sheets(sheetName)
On Error GoTo 0 ' ← 必ず直後に解除
SheetExists = Not (ws Is Nothing)
Set ws = Nothing
End Function
' ブックが開かれているかの確認
Function WorkbookIsOpen(wbName As String) As Boolean
Dim wb As Workbook
On Error Resume Next
Set wb = Workbooks(wbName)
On Error GoTo 0
WorkbookIsOpen = Not (wb Is Nothing)
Set wb = Nothing
End Function
' 使用例
Sub UseExistenceCheck()
If SheetExists("集計") Then
Debug.Print "集計シートが存在します"
Else
MsgBox "集計シートが見つかりません。作成しますか?", vbYesNo
End If
If WorkbookIsOpen("売上データ.xlsx") Then
Debug.Print "売上データ.xlsxは開かれています"
Else
MsgBox "売上データ.xlsxを先に開いてください。", vbExclamation
End If
End Sub
正しい使用パターン:Collectionのキー確認
' ========================================
' 正しい使い方2:CollectionやDictionaryのキー確認
' ========================================
Function CollectionKeyExists(col As Collection, key As String) As Boolean
Dim dummy As Variant
On Error Resume Next
dummy = col(key)
CollectionKeyExists = (Err.Number = 0)
Err.Clear ' 確認後にクリア
On Error GoTo 0
End Function
' 使用例
Sub UseCollectionCheck()
Dim col As New Collection
col.Add "東京", "tokyo"
col.Add "大阪", "osaka"
If CollectionKeyExists(col, "tokyo") Then
Debug.Print "キー'tokyo'の値: " & col("tokyo")
End If
If Not CollectionKeyExists(col, "nagoya") Then
Debug.Print "キー'nagoya'は存在しません"
End If
End Sub
正しい使用パターン:外部オブジェクトのインスタンス確認
' ========================================
' 正しい使い方3:外部アプリの存在確認
' ========================================
Function OutlookIsAvailable() As Boolean
Dim obj As Object
On Error Resume Next
Set obj = CreateObject("Outlook.Application")
OutlookIsAvailable = (Err.Number = 0)
Err.Clear
On Error GoTo 0
If Not obj Is Nothing Then Set obj = Nothing
End Function
' 正しい使い方4:数値変換の失敗を無視して0を返す
Function TryParseDouble(val As Variant, Optional defaultVal As Double = 0) As Double
On Error Resume Next
Err.Clear
TryParseDouble = CDbl(val)
If Err.Number <> 0 Then
TryParseDouble = defaultVal
Err.Clear
End If
On Error GoTo 0
End Function
Sub UseConvert()
Debug.Print TryParseDouble("123.45") ' → 123.45
Debug.Print TryParseDouble("abc", -1) ' → -1
Debug.Print TryParseDouble(Empty, 0) ' → 0
End Sub
On Error Resume Nextの正しい使い方・守るべき3原則
- 原則1:使用範囲は最小限に:
On Error Resume Nextはエラーが予想される1〜3行だけに限定する。有効にした直後にOn Error GoTo 0で解除する - 原則2:Err.Clearをチェック前に必ず呼ぶ:前のエラーが残っていると誤判定の原因になる。
On Error Resume Nextの直後にErr.Clearを書く習慣をつける - 原則3:確認後は必ず解除する:
On Error Resume Nextを有効にしたら、使い終わった直後に必ずOn Error GoTo 0で解除する。プロシージャ末尾まで有効なままにしない
On Error GoToによる本格的なエラーハンドリング
On Error Resume Next が「局所的な存在確認」に使うツールであるのに対して、プロシージャ全体のエラー処理には On Error GoTo ラベル を使います。こちらはエラーが発生した瞬間に指定したラベルにジャンプするため、エラーの見落としが起きません。
' ========================================
' On Error GoToによる標準的なエラーハンドリングパターン
' ========================================
Sub StandardErrorHandling()
' ---- 変数宣言 ----
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
' ---- 高速化設定 ----
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
' ---- エラーハンドラを登録 ----
On Error GoTo ErrorHandler
' ===== メイン処理 =====
Set ws = ThisWorkbook.Sheets("データ入力")
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim srcData As Variant
Dim outData As Variant
srcData = ws.Range("A2:B" & lastRow).Value
ReDim outData(1 To UBound(srcData, 1), 1 To 1)
For i = 1 To UBound(srcData, 1)
outData(i, 1) = CDbl(srcData(i, 1)) * CDbl(srcData(i, 2))
Next i
ws.Range("C2:C" & lastRow).Value = outData
' =====================
MsgBox "処理完了(" & lastRow - 1 & " 行)", vbInformation
GoTo CleanUp
ErrorHandler:
' エラー情報を変数に保存(CleanUpでErrオブジェクトがリセットされる前に)
Dim errNum As Long
Dim errDesc As String
Dim errLine As String
errNum = Err.Number
errDesc = Err.Description
errLine = "処理行: " & i & " 行目" ' ループ変数で位置を特定
MsgBox "エラーが発生しました。" & vbCrLf & vbCrLf & _
"番号: " & errNum & vbCrLf & _
"内容: " & errDesc & vbCrLf & _
errLine, vbCritical
CleanUp:
' ---- 設定を元に戻す(エラー時も必ず実行) ----
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
Set ws = Nothing
End Sub
On Error GoToとOn Error Resume Nextを組み合わせる実践パターン
' ========================================
' On Error GoToをメインに使いつつ、
' 特定の操作だけOn Error Resume Nextで囲むパターン
' ========================================
Sub CombinedErrorHandling()
Dim ws As Worksheet
Dim wsOut As Worksheet
Dim lastRow As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
On Error GoTo ErrorHandler ' メインのエラーハンドラ
' === シート取得(存在しない場合は作成する) ===
' ここだけOn Error Resume Nextを局所的に使う
On Error Resume Next
Err.Clear
Set wsOut = ThisWorkbook.Sheets("結果出力")
On Error GoTo ErrorHandler ' ← 局所的使用後、すぐにGoToハンドラに戻す
' シートが存在しなければ作成する
If wsOut Is Nothing Then
Set wsOut = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsOut.Name = "結果出力"
End If
' === メインの処理(エラーが起きれば ErrorHandler にジャンプ) ===
Set ws = ThisWorkbook.Sheets("データ入力")
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim data As Variant
data = ws.Range("A2:C" & lastRow).Value
wsOut.Cells.Clear
wsOut.Range("A2").Resize(UBound(data, 1), UBound(data, 2)).Value = data
MsgBox "処理完了", vbInformation
GoTo CleanUp
ErrorHandler:
MsgBox "エラー " & Err.Number & ": " & Err.Description, vbCritical
CleanUp:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Set ws = Nothing
Set wsOut = Nothing
End Sub
エラーログを自動記録する仕組みを作る
「どこでエラーが起きているか後から確認したい」「エラーが起きても処理を続けながら最後にまとめて報告したい」という場合は、エラー情報をログとして記録する仕組みを作ると便利です。
' ========================================
' エラーログを記録しながら処理を続行するパターン
' 全行を処理して最後にエラー一覧を表示する
' ========================================
Sub ProcessWithErrorLog()
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
Dim errLog As String ' エラーログを文字列で蓄積
Dim errCount As Long
Set ws = ThisWorkbook.Sheets(1)
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
errLog = ""
errCount = 0
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
For i = 2 To lastRow
On Error Resume Next
Err.Clear
' 処理(型変換を含む演算)
Dim val1 As Double, val2 As Double
val1 = CDbl(ws.Cells(i, 1).Value)
val2 = CDbl(ws.Cells(i, 2).Value)
If Err.Number = 0 Then
ws.Cells(i, 3).Value = val1 * val2
Else
' エラーをログに追記
errCount = errCount + 1
errLog = errLog & i & "行目: " & ws.Cells(i, 1).Value & _
" / " & ws.Cells(i, 2).Value & _
" → エラー" & Err.Number & ":" & Err.Description & vbCrLf
ws.Cells(i, 3).Value = "" ' エラー行は空欄
Err.Clear
End If
On Error GoTo 0
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
' 最後にエラーをまとめて報告
If errCount > 0 Then
MsgBox errCount & " 件のエラーが発生しました。" & vbCrLf & vbCrLf & errLog, _
vbExclamation, "処理完了(エラーあり)"
Else
MsgBox "処理完了(エラーなし)", vbInformation
End If
Set ws = Nothing
End Sub
' ========================================
' エラーをシートのログシートに記録する恒久的な仕組み
' ========================================
Sub LogErrorToSheet(errNum As Long, errDesc As String, _
location As String, Optional detail As String = "")
Dim wsLog As Worksheet
' ログシートの取得または作成
On Error Resume Next
Set wsLog = ThisWorkbook.Sheets("エラーログ")
On Error GoTo 0
If wsLog Is Nothing Then
Set wsLog = ThisWorkbook.Sheets.Add( _
After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsLog.Name = "エラーログ"
wsLog.Range("A1:E1").Value = Array("日時", "エラー番号", "説明", "発生箇所", "詳細")
wsLog.Range("A1:E1").Font.Bold = True
End If
' 最終行の次に追記
Dim nextRow As Long
nextRow = wsLog.Cells(wsLog.Rows.Count, 1).End(xlUp).Row + 1
wsLog.Cells(nextRow, 1).Value = Format(Now(), "yyyy/mm/dd HH:mm:ss")
wsLog.Cells(nextRow, 2).Value = errNum
wsLog.Cells(nextRow, 3).Value = errDesc
wsLog.Cells(nextRow, 4).Value = location
wsLog.Cells(nextRow, 5).Value = detail
wsLog.Columns("A:E").AutoFit
Set wsLog = Nothing
End Sub
' 使用例
Sub ProcessWithSheetLog()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)
Dim i As Long
For i = 2 To 1000
On Error Resume Next
Err.Clear
Dim result As Double
result = CDbl(ws.Cells(i, 1).Value) / CDbl(ws.Cells(i, 2).Value)
If Err.Number <> 0 Then
LogErrorToSheet Err.Number, Err.Description, _
"ProcessWithSheetLog", _
i & "行目: " & ws.Cells(i, 1).Value & " / " & ws.Cells(i, 2).Value
result = 0
Err.Clear
End If
On Error GoTo 0
ws.Cells(i, 3).Value = result
Next i
Set ws = Nothing
MsgBox "処理完了。エラーログシートを確認してください。", vbInformation
End Sub
On Error Resume Next地獄から脱出するデバッグ手順
既存のコードが On Error Resume Next だらけで、どこで何が起きているか完全にわからなくなっている場合の、体系的な脱出手順を解説します。
ステップ1:コード全体のOn Error Resume Nextをすべてコメントアウトする
まずすべての On Error Resume Next をコメントアウトしてから実行し、どのエラーが最初に止まるかを確認します。VBAエディタの「編集」→「置換」(Ctrl+H)で一括検索・コメントアウトできます。
' 一括コメントアウトの検索・置換設定
' 検索する文字列: On Error Resume Next
' 置換後の文字列: 'On Error Resume Next
' → これで全件をコメントアウトできる
ステップ2:最初に止まったエラーを1つずつ修正する
エラーが止まった行を確認し、原因を修正します。修正できたら再実行して次のエラーに進みます。この作業を繰り返してエラーをゼロにしていきます。
ステップ3:本当にOn Error Resume Nextが必要な箇所だけ復活させる
「存在確認」など、意図的にエラーを利用している箇所にのみ On Error Resume Next を復活させます。その際、前述の「3原則」を守って局所的に使います。
ステップ4:残りのエラー処理をOn Error GoToに切り替える
' ========================================
' On Error Resume Nextをリファクタリングする
' 「あとで直す」と思ってそのままにしがちな部分を
' 正しいOn Error GoToに書き換えるテンプレート
' ========================================
' ---- Before(アンチパターン)----
Sub BeforeRefactor()
On Error Resume Next
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("データ")
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim i As Long
For i = 1 To lastRow
ws.Cells(i, 2).Value = CDbl(ws.Cells(i, 1).Value) * 1.1
Next i
End Sub
' ---- After(正しいパターン)----
Sub AfterRefactor()
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
On Error GoTo ErrorHandler
' シートの存在確認は局所的なOn Error Resume Nextを使う
On Error Resume Next
Err.Clear
Set ws = ThisWorkbook.Sheets("データ")
On Error GoTo ErrorHandler ' 即座に通常のエラーハンドラに戻す
If ws Is Nothing Then
MsgBox "「データ」シートが見つかりません。", vbCritical
GoTo CleanUp
End If
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim srcData As Variant
Dim outData As Variant
srcData = ws.Range("A1:A" & lastRow).Value
ReDim outData(1 To lastRow, 1 To 1)
For i = 1 To lastRow
If IsNumeric(srcData(i, 1)) Then
outData(i, 1) = CDbl(srcData(i, 1)) * 1.1
Else
outData(i, 1) = 0
End If
Next i
ws.Range("B1:B" & lastRow).Value = outData
MsgBox "完了(" & lastRow & "行)", vbInformation
GoTo CleanUp
ErrorHandler:
MsgBox "エラー " & Err.Number & ": " & Err.Description, vbCritical
CleanUp:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Set ws = Nothing
End Sub
プロジェクト全体のエラーハンドリング設計
個別のプロシージャのエラー処理を正しく書くだけでなく、プロジェクト全体で一貫したエラーハンドリングの設計をすることで、デバッグと保守が格段に楽になります。
共通エラーハンドラ関数の実装
' ========================================
' プロジェクト共通エラーハンドラ
' 標準モジュール(modErrorHandler)に記述する
' ========================================
Option Explicit
' エラー処理の共通関数
Public Sub HandleError( _
ByVal errNum As Long, _
ByVal errDesc As String, _
ByVal procName As String, _
Optional ByVal detail As String = "", _
Optional ByVal showMsg As Boolean = True, _
Optional ByVal logToSheet As Boolean = True)
Dim fullMsg As String
fullMsg = "【" & procName & "】" & vbCrLf & _
"エラー番号: " & errNum & vbCrLf & _
"内 容: " & errDesc
If Len(detail) > 0 Then
fullMsg = fullMsg & vbCrLf & "詳 細: " & detail
End If
' ログシートへの記録
If logToSheet Then
LogErrorToSheet errNum, errDesc, procName, detail
End If
' デバッグウィンドウへの出力
Debug.Print Format(Now(), "HH:mm:ss") & " ERROR - " & fullMsg
' メッセージボックス表示
If showMsg Then
MsgBox fullMsg, vbCritical, "エラーが発生しました"
End If
End Sub
' エラー番号の種類別メッセージ
Public Function GetFriendlyErrorMsg(errNum As Long) As String
Select Case errNum
Case 9 : GetFriendlyErrorMsg = "シート名や配列の添字が正しくありません。名前を確認してください。"
Case 11 : GetFriendlyErrorMsg = "ゼロ除算が発生しました。分母がゼロになっていないか確認してください。"
Case 13 : GetFriendlyErrorMsg = "データの型が一致しません。数値が必要な場所に文字列が入っていないか確認してください。"
Case 53 : GetFriendlyErrorMsg = "ファイルが見つかりません。パスとファイル名を確認してください。"
Case 75 : GetFriendlyErrorMsg = "フォルダのパスが無効です。フォルダの存在を確認してください。"
Case 91 : GetFriendlyErrorMsg = "オブジェクトが設定されていません。Setで代入してから使用してください。"
Case 438 : GetFriendlyErrorMsg = "プロパティまたはメソッドが存在しません。名前のスペルを確認してください。"
Case 1004: GetFriendlyErrorMsg = "Excelオブジェクトへの操作が失敗しました。シートの保護やセル範囲を確認してください。"
Case Else: GetFriendlyErrorMsg = "予期しないエラーです。"
End Select
End Function
' ========================================
' 共通エラーハンドラを使ったプロシージャのテンプレート
' ========================================
Sub ProcessTemplate()
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
On Error GoTo ErrorHandler
' ===== メイン処理 =====
Set ws = ThisWorkbook.Sheets("データ")
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim srcData As Variant
srcData = ws.Range("A2:B" & lastRow).Value
Dim outData As Variant
ReDim outData(1 To UBound(srcData, 1), 1 To 1)
For i = 1 To UBound(srcData, 1)
outData(i, 1) = CDbl(srcData(i, 1)) + CDbl(srcData(i, 2))
Next i
ws.Range("C2:C" & lastRow).Value = outData
' =====================
MsgBox "処理完了", vbInformation
GoTo CleanUp
ErrorHandler:
HandleError Err.Number, Err.Description, "ProcessTemplate", _
"処理行: " & i & " 行目"
CleanUp:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
Set ws = Nothing
End Sub
On Error Resume Next チェックリスト
既存コードを見直すとき、または新しいコードを書くとき、以下のリストで確認してください。
On Error Resume Nextがプロシージャ全体を囲んでいないか(先頭に書いてそのまま末尾まで有効な状態になっていないか)On Error Resume Nextの後に必ずErr.Clearを呼んでいるか- エラーチェックの直前に
Err.Clearを入れているか(前のエラーが残っていないか) - エラーが発生したかどうかを
If Err.Number <> 0 Thenで確認しているか - 確認後に
On Error GoTo 0またはOn Error GoTo ErrorHandlerで解除しているか On Error Resume Nextの有効範囲が 1〜3行程度の必要最小限になっているか- プロシージャ全体のエラー処理に
On Error GoTo ErrorHandlerを使っているか - エラーハンドラ内で
Err.NumberとErr.Descriptionを変数に保存してから処理しているか(CleanUpでリセットされる前に) - エラー発生時も
CleanUpラベルで設定が元に戻るよう設計されているか - エラーをログ記録する仕組みがあるか(シートログ・イミディエイトウィンドウ出力)
- 「どこでエラーが出ているか特定できない」と感じたら、すぐに
On Error Resume NextをコメントアウトしてF8ステップ実行する習慣があるか
まとめ
On Error Resume Next は「エラーを無視する命令」ではなく、「エラーが起きても次の行に進む命令」です。使い方を誤るとエラーが積み重なって原因の特定が困難になりますが、正しい使い方を理解すれば強力なツールになります。本記事の要点をまとめます。
- エラー箇所の特定:
On Error Resume NextをコメントアウトしてF8ステップ実行するのが最速。または各行の後にErr.Numberチェックを挟んでトレースする - Err.Clearは必須:
On Error Resume Nextの直後と各エラーチェックの直前にErr.Clearを呼ぶ。これを忘れると前のエラーが残り続ける - 使用範囲は最小限:
On Error Resume Nextは「存在確認」などエラーが予想される1〜3行だけに限定し、直後にOn Error GoTo 0またはOn Error GoTo ErrorHandlerで解除する - プロシージャ全体はOn Error GoTo:メインの処理全体のエラーハンドリングには
On Error GoTo ErrorHandlerを使う。これならエラーが起きた瞬間に確実にハンドラに飛ぶ - エラーログ:ループ内でエラーをスキップしながら処理を続けたい場合は、エラー情報を文字列またはログシートに蓄積して最後にまとめて報告する設計にする
- 共通エラーハンドラ:プロジェクト全体で
HandleErrorのような共通関数を用意してエラー記録・表示を一元管理すると保守性が大きく向上する
「On Error Resume Next を書いたらどこでエラーが出てるかわからなくなった」という状況は、この記事の手順に従えば必ず原因を特定できます。そして再発防止のために、On Error Resume Next の使用箇所を局所的に限定し、プロシージャ全体のエラー管理を On Error GoTo に置き換えることが根本的な解決策です。