VBAエラー処理3パターン比較──マクロの"突然停止"を95%防いだ実践テクニック
納品前夜、マクロが止まった
あれは3年前の金曜夜だった。
クライアント先で動いていた請求書自動作成マクロが、本番データを流した瞬間に止まった。画面には「実行時エラー '1004'」の文字。原因は、1,200件中たった1件だけ入っていた空白セルだった。
正直、血の気が引いた。テスト環境では完璧に動いていたのに。
「エラー処理、ちゃんと入れておけばよかった」──この後悔が、私のVBAコードを根本から変えた。あの日から、マクロが予期せず止まる事故はほぼゼロになっている。
この記事では、VBAのエラー処理3パターンを比較しながら、「マクロの突然死」を防ぐ具体的な方法を話す。
そもそもVBAのエラー処理、なぜ後回しにしがちなのか
「とりあえず動くものを作りたい」って気持ち、わかる。
Excel VBAを書く人の多くは専業プログラマーじゃない。経理の人、営業事務の人、総務の人。目の前の手作業をなんとか自動化したくてコードを書いている。エラー処理なんて後回しになって当然だと思う。
ただ、ここが落とし穴だったりする。
エラー処理なしのマクロは、想定外のデータが1件でも混じると即停止する。私の経験だと、業務マクロが止まる原因の8割は「想定外のデータ」だった。空白セル、全角数字、日付のフォーマット違い。こういう"ちょっとした例外"でマクロは簡単に死ぬ。
VBAエラー処理の3パターン、ざっくり整理
VBAには大きく3つのエラー処理パターンがある。
パターン1:On Error Resume Next(エラーを無視して続行)
vb
Sub サンプル1_ResumeNext()
Dim i As LongOn Error Resume Next ' エラーが出ても次の行へ進む
For i = 2 To 100
Cells(i, 3).Value = Cells(i, 1).Value / Cells(i, 2).Value
Next i
On Error GoTo 0 ' エラー無視を解除
End Sub
```
これは「エラーが起きても止まらず次の行へ進む」という指示。一番シンプルで、一番危険なパターンでもある。
なぜ危険か。エラーが起きたことに気づけないから。ゼロ除算が起きても、型の不一致が起きても、何事もなかったかのように処理が進む。結果、出力データが壊れていても誰も気づかない。
私も最初はこれを多用していた。そしたら「計算結果がおかしい」とクライアントから連絡が来て、原因調査に丸一日かかった。Resume Nextで握りつぶされたエラーを追跡するのは本当につらい。
使っていい場面: オブジェクトの存在チェックなど、エラーが起きること自体が「情報」になる場面だけ。
vb
' シートが存在するか確認する例
Dim ws As Worksheet
On Error Resume Next
Set ws = ThisWorkbook.Sheets("集計")
On Error GoTo 0If ws Is Nothing Then
MsgBox "「集計」シートが見つかりません", vbExclamation
Exit Sub
End If
```
こういう「あるかないか調べたいだけ」のときは便利。ただし、`On Error GoTo 0` で必ずエラー無視を解除すること。これを忘れると、以降のコード全体がエラーを握りつぶす爆弾になる。
パターン2:On Error GoTo ラベル(エラー発生時にジャンプ)
vb
Sub サンプル2_GoToラベル()
On Error GoTo エラー処理 Dim wb As Workbook
Set wb = Workbooks.Open("C:\売上データ\2024年3月.xlsx")
' ここにメインの処理を書く
wb.Sheets(1).Range("A1:D100").Copy ThisWorkbook.Sheets(1).Range("A1")
wb.Close SaveChanges:=False
Exit Sub ' ←これを忘れると、正常時もエラー処理に突入する
エラー処理:
MsgBox "エラーが発生しました" & vbCrLf & _
"エラー番号: " & Err.Number & vbCrLf & _
"内容: " & Err.Description, vbCritical
End Sub
```
これが一番よく使われるパターン。エラーが起きたら指定したラベルにジャンプして、そこでエラー内容を表示したり、ログに書いたりできる。
ここで余談なんだけど、`Exit Sub` を書き忘れる人がめちゃくちゃ多い。私も最初やらかした。書き忘れると、正常に処理が終わったのにエラーメッセージが表示されるという謎の挙動になる。「バグです!」と報告が来て、原因が `Exit Sub` の書き忘れだったときの恥ずかしさといったら。
使っていい場面: ファイルを開く、外部データベースに接続する、印刷するなど、外部リソースに依存する処理全般。
パターン3:Errオブジェクトを組み合わせた本格的なエラー処理
vb
Sub サンプル3_本格エラー処理()
On Error GoTo エラー処理 Dim i As Long
Dim errCount As Long
errCount = 0
For i = 2 To 1000
On Error Resume Next
Cells(i, 4).Value = _
Application.WorksheetFunction.VLookup( _
Cells(i, 1).Value, Sheets("マスタ").Range("A:C"), 3, False)
If Err.Number <> 0 Then
Cells(i, 5).Value = "エラー: " & Err.Description
errCount = errCount + 1
Err.Clear ' エラー情報をリセット
End If
On Error GoTo エラー処理 ' エラーハンドラを再設定
Next i
If errCount > 0 Then
MsgBox errCount & "件のエラーがありました。E列を確認してください", vbInformation
End If
Exit Sub
エラー処理:
MsgBox "想定外のエラーが発生しました" & vbCrLf & _
"行番号: " & i & vbCrLf & _
"エラー: " & Err.Number & " - " & Err.Description, vbCritical
End Sub
```
これが私のおすすめ。Resume Nextで個別のエラーを拾いつつ、Errオブジェクトで内容を記録し、想定外のエラーはGoToラベルでキャッチする。3つのパターンのいいとこ取りだ。
`Err.Number` でエラーの種類がわかるし、`Err.Description` で内容もわかる。`Err.Clear` でリセットすれば次のループに影響しない。
冒頭の請求書マクロも、このパターンに書き直したら、1,200件中エラーのある行だけスキップして残りを正常処理できるようになった。止まらない。しかもどこでエラーが出たか一目瞭然。
3パターン、どう使い分けるか
結論から言うと、「パターン3をベースにして、場面に応じてパターン1・2を混ぜる」のが正解だと思っている。
**Resume Next単体で使うケース**
→ オブジェクトの存在確認、1〜2行で完結する処理のみ。必ず直後にOn Error GoTo 0を書く。
**GoToラベルで十分なケース**
→ ループのない一本道の処理。ファイルを開いて、加工して、保存して終わり、みたいなもの。
**Errオブジェクトまで使うケース**
→ ループ内で大量データを処理するとき。1件のエラーで全体を止めたくないとき。
考えてみてほしい。1万件のデータを処理するマクロで、9,999件目にエラーが出て全部やり直しになったら? エラー処理があれば、その1件だけスキップして残りは正常に完了する。この差は大きい。
実務で使えるテンプレート──コピペしてすぐ使える
私が実際の案件で使っているテンプレートを載せておく。
vb
Sub 業務処理テンプレート()
' --- 初期設定 ---
On Error GoTo 想定外エラー Dim startTime As Double
startTime = Timer
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
Dim errLog As String
Dim errCount As Long
Dim i As Long
' --- メインループ ---
For i = 2 To lastRow
On Error Resume Next
'=== ここに実際の処理を書く ===
' 例: Cells(i, 3).Value = Cells(i, 1).Value * Cells(i, 2).Value
'================================
If Err.Number <> 0 Then
errLog = errLog & "行" & i & ": " & Err.Description & vbCrLf
errCount = errCount + 1
Err.Clear
End If
On Error GoTo 想定外エラー
Next i
' --- 後処理 ---
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Dim elapsed As Double
elapsed = Round(Timer - startTime, 1)
If errCount > 0 Then
MsgBox "処理完了(" & elapsed & "秒)" & vbCrLf & _
errCount & "件のエラーあり:" & vbCrLf & errLog, vbExclamation
Else
MsgBox "正常に完了しました(" & elapsed & "秒、" & _
lastRow - 1 & "件処理)", vbInformation
End If
Exit Sub
想定外エラー:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
MsgBox "想定外のエラーが発生しました" & vbCrLf & _
"場所: 行" & i & vbCrLf & _
"Err " & Err.Number & ": " & Err.Description, vbCritical
End Sub
```
このテンプレートには、処理時間の計測も入っている。「このマクロ、何秒で終わるの?」と聞かれたときにサッと答えられる。地味に便利。
やりがちな失敗3つ──私も全部やった
On Error GoTo 0 の書き忘れ
Resume Nextを書いたまま解除し忘れるパターン。後続の処理でバグが出ても気づけない。これ、デバッグが本当に地獄になる。「動いてるのに結果がおかしい」という最悪の状況になる。
Exit Sub の書き忘れ
さっきも書いたけど、本当に多い。正常終了してもエラーメッセージが出る。ユーザーは「毎回エラーが出るんですけど」と混乱する。
Err.Clear の書き忘れ
ループ内でErrオブジェクトをチェックするとき、`Err.Clear` を忘れると前のループのエラー情報が残ったまま次のループに入る。結果、エラーじゃない行もエラー扱いになる。
この3つ、紙に書いてモニターに貼っておくだけでバグが激減する。冗談じゃなく、本当に効く。
エラー処理を入れたら何が変わったか
数字で話すと、私のクライアント案件では:
マクロの「突然停止」による業務中断が月4〜5回 → ほぼ0回に(95%減)
- エラー発生時の原因調査が平均2時間 → 10分に短縮(エラーログのおかげ)
エラー処理は「めんどくさい追加作業」じゃない。未来の自分を助ける投資だ。
ところが、ここで正直に言うと、エラー処理を入れるのが習慣になるまで3ヶ月くらいかかった。最初は「あとで入れよう」と思って結局入れない、の繰り返し。だからテンプレートを作った。テンプレートがあれば、最初からエラー処理込みで書き始められる。
明日からやること
記事を読んだだけでは何も変わらない。だから、具体的なアクションを1つだけ提案する。
**今動いているマクロの中で、一番よく使うものを1つ選んで、上のテンプレートのエラー処理を追加してみてほしい。**
全部のマクロに一気に入れようとしなくていい。まず1つ。それで「あ、エラーが出てもちゃんと処理が続くじゃん」と実感できたら、自然と他のマクロにも入れたくなるはず。
もっとExcelスキルを磨きたい方へ
エラー処理を入れるだけで、マクロの信頼性は劇的に上がる。「動いたり動かなかったりするマクロ」から「何があっても結果を返すマクロ」に変わる。
有料記事では、今回のテンプレートの応用パターン(ログファイル出力版、メール通知版、リトライ処理付き版)と、実務でよく遭遇するエラー番号の早見表をまとめている。
もし興味があれば覗いてみてほしい。ただ、まずはこの記事のテンプレートを実際に使ってみて、効果を実感してからで全然OK。焦る必要はない。
