VBAのエラーハンドリング完全ガイド|On Error GoToとResume Nextの使い分け

スポンサーリンク

「実行時エラー ’13’: 型が一致しません」「実行時エラー ‘1004’: アプリケーション定義またはオブジェクト定義のエラーです」。VBAマクロを書きはじめると、こうした突然のエラー停止に何度もぶつかります。

そのたびに On Error Resume Next を貼り付けて凌いでいませんか。それはたぶん、地雷原の上に絨毯を敷いているようなものです。

この記事では、VBAのエラーハンドリングを On Error GoTo On Error Resume Next Err オブジェクト の3要素で整理します。実務でよく遭遇する5つのシーンごとに、コピペで使える雛形コードと使い分け基準をまとめて紹介します。読み終わるころには、自信を持ってエラー処理を設計できるようになります。

VBAエラーハンドリングとは?

VBAエラーハンドリングとは、マクロ実行中に発生する想定外のエラーを検出する仕組みです。プログラムを止めずに、ログ出力・代替処理・終了などへ適切に分岐させます。

エラー処理を入れていないマクロは、想定外の入力やファイル不在に遭遇すると、その瞬間に「実行時エラー」のダイアログを出して止まります。配布した利用者からすると、何が起きたのか分からないまま処理が中断されるため、信頼を一気に失います。

エラーハンドリングが必要な理由

実務でVBAを使う場面は、想定外が起きやすい環境です。対象のファイルが毎日変わったり、他人が触ったブックを処理したりするからです。エラーハンドリングを仕込んでおくと、次のようなメリットがあります。

  • ユーザーに分かりやすいメッセージを出して落ち着かせられる
  • 途中で止まらず、残りの処理を最後まで通せる
  • ログにエラー番号と発生場所を記録して、後から原因調査ができる
  • 万が一のときも、Excelをフリーズさせずに正常終了できる

VBAで発生する3種類のエラー

VBAで発生するエラーは、大きく3つに分類できます。

種類発生タイミング主な例エラーハンドリング対象
コンパイルエラー実行前(コードチェック時)End If 抜け、Dim 漏れ対象外(コード修正で解決)
実行時エラー実行中13型不一致、1004、9、91、438対象
論理エラー実行は通るが結果が違うIf条件の書き間違い対象外(テストで検出)

エラーハンドリングが対応するのは、原則として 実行時エラー だけです。コンパイルエラーは事前にコードを直せば消えます。論理エラーは「動くけど結果が間違っている」ためエラーとして検出されません。

エラーハンドリングで使う3つの主要構文

VBAのエラーハンドリングは、次の3つの構文要素を組み合わせて構築します。

  • On Error GoTo ラベル: エラー発生時に指定ラベルへジャンプする
  • On Error Resume Next: エラー発生行をスキップして次の行から続行する
  • Err オブジェクト: 直近のエラー番号・説明・発生元を保持する

これに加えて、ハンドラを解除する On Error GoTo 0 と、ハンドラ内から実行を再開する Resume / Resume Next / Resume ラベル が補助的に使われます。

NOTE

VBAには .NET のような Try-Catch 構文はありません。代わりに On Error GoTo ラベルとExit Subを組み合わせて疑似的に実装します。

On Error GoToでエラー処理ラベルへジャンプする

On Error GoTo は、エラー発生時にあらかじめ用意したラベル位置へジャンプさせる構文です。最も基本的なエラーハンドリング構文として位置付けられます。

エラーが起きたときに「専用の処理コーナー」に移動して、そこでメッセージ表示やログ書き込みを行うイメージです。Try-CatchCatch ブロックに相当します。

基本構文と動作の流れ

最小構成のサンプルです。

Sub Sample_OnErrorGoTo()
    '--- エラーハンドラを設定 ---
    On Error GoTo ErrHandler

    Dim n As Integer
    n = CInt("abc")  '型不一致エラー(実行時エラー 13)

    MsgBox "正常終了"
    Exit Sub  '正常時はここで抜ける

ErrHandler:
    '--- エラー発生時の処理 ---
    MsgBox "エラー番号: " & Err.Number & vbCrLf & _
           "内容: " & Err.Description
End Sub

CInt("abc") は文字列を数値に変換できないため、実行時エラー13を発生させます。On Error GoTo ErrHandler を先に書いておくと、エラーが起きた瞬間に ErrHandler: ラベル位置へ処理が飛びます。そして、メッセージボックスが表示されます。

Sub構造の正しい書き方(Exit Subとセット)

On Error GoTo を使うときに必ず守ってほしいのが、正常終了の直前に Exit Sub を入れる ことです。

Sub Sample_Structure()
    On Error GoTo ErrHandler

    '--- ① 通常処理 ---
    Range("A1").Value = "OK"

    '--- ② 正常終了:必ず Exit Sub で抜ける ---
    Exit Sub

ErrHandler:
    '--- ③ エラー処理 ---
    MsgBox "エラー: " & Err.Description
End Sub

Exit Sub を書き忘れると、正常時もそのまま ErrHandler: ラベルに突入してしまいます。エラーが起きていないのに「エラー: 」というメッセージが出て、利用者を混乱させる原因になります。

WARNING

On Error GoTo ラベル を使うときは、必ず正常処理の終わりに Exit Sub を入れてください。これを忘れると、正常時にもエラーメッセージが出る原因になります。

On Error GoTo 0でハンドリングを解除する

On Error GoTo 0 は、現在のプロシージャ内で有効になっているエラーハンドリングを解除する構文です。

Sub Sample_GoTo0()
    On Error Resume Next
    '--- ここはエラー無視 ---
    Worksheets("存在しないシート").Activate

    On Error GoTo 0
    '--- ここから先はエラー無視を解除 ---
    Range("A1").Value = 100
End Sub

On Error GoTo 00 は数字のゼロで、ラベル名ではありません。そのため、コード内に 0: というラベルを書く必要はありません。「ハンドリングをいったん解除する」専用のキーワードとして覚えておきましょう。

On Error Resume Nextでエラーを無視する

On Error Resume Next は、エラー発生行をスキップして次の行から実行を継続する構文です。

「とりあえず動かしたい」「エラーは起きるけど無視したい」という場面で多用されます。一方で、使い方を誤ると バグを見えなくする最も危険な書き方 にもなります。

基本構文と使いどころ

ファイル削除やシート存在チェックなど、「失敗しても次に進めたい」処理で使うのが本来の用途です。

Sub Sample_ResumeNext()
    Dim ws As Worksheet

    '--- シート取得を試行 ---
    On Error Resume Next
    Set ws = Worksheets("売上データ")
    On Error GoTo 0  'すぐに解除する

    '--- 取得できたかをErrではなくIs Nothingで判定 ---
    If ws Is Nothing Then
        MsgBox "シート『売上データ』が存在しません"
        Exit Sub
    End If

    ws.Range("A1").Value = "OK"
End Sub

ポイントは、シート取得を試みた直後に On Error GoTo 0 で解除していることです。そのうえで、If ws Is Nothing Then を使って結果を判定しています。

Resume Nextの危険性と注意点

On Error Resume Next を Sub の先頭に1行貼り付けて、それ以降すべてエラー無視にする書き方があります。これは絶対にやめてください。

Sub BadExample()
    On Error Resume Next  'NG: 全文無視は危険

    '--- 以下、すべてのエラーが見えなくなる ---
    Range("A1").Value = "テスト"
    Worksheets("存在しないシート").Activate
    Workbooks.Open "C:nonexistent.xlsx"
    '何もエラーが出ないが、何も処理されていない
End Sub

このコードは一見「エラーが出ないから動いている」ように見えます。しかし実際は、シートを開けず、ファイルも読めず、ただ何もしていない状態です。

実務での被害例として、「データ転記マクロが何ヶ月も空のシートを出し続けていた」というケースがあります。原因はリファクタ時に紛れ込んだ On Error Resume Next でした。

局所的に使うブロック化テクニック

On Error Resume Next を使うときは、次の3点セットで囲むのが鉄則です。

  1. On Error Resume Next でブロック開始
  2. 試したい処理を 数行 だけ書く
  3. On Error GoTo 0 で必ず解除する
Sub Sample_BlockResumeNext()
    Dim wb As Workbook

    '--- ブロック開始 ---
    On Error Resume Next
    Set wb = Workbooks.Open("C:reportssales.xlsx")
    On Error GoTo 0
    '--- ブロック終了 ---

    '--- ブロックの結果を判定 ---
    If wb Is Nothing Then
        MsgBox "ファイルを開けませんでした"
        Exit Sub
    End If

    MsgBox "開けました: " & wb.Name
End Sub

「Resume Next を使ってよいのは、すぐに On Error GoTo 0 するときだけ」と覚えておけば、まず事故は起こりません。

ErrオブジェクトでエラーNo・説明を取得する

Err オブジェクトは、直近に発生したエラーの情報を保持するVBA組み込みオブジェクトです。エラーハンドラ内で Err.Number を見れば、どんなエラーが起きたのかをコードで判定できます。

Err.Number / Err.Description / Err.Sourceの使い方

主要なプロパティは3つです。

プロパティ戻り値の型意味
Err.NumberLongエラー番号(例: 13、1004、9、91、438)
Err.DescriptionStringエラーの説明文(例: “型が一致しません”)
Err.SourceStringエラー発生元の名前(例: “VBAProject”)

エラーハンドラ内で番号別に分岐させるサンプルです。

Sub Sample_ErrObject()
    On Error GoTo ErrHandler

    Dim n As Integer
    n = CInt("abc")  'エラー13発生

    Exit Sub

ErrHandler:
    '--- エラー番号で処理を分岐 ---
    Select Case Err.Number
        Case 13
            MsgBox "数値変換に失敗しました: " & Err.Description
        Case 9
            MsgBox "シートが見つかりません: " & Err.Description
        Case Else
            MsgBox "想定外のエラー [" & Err.Number & "]: " & _
                   Err.Description
    End Select
End Sub

Select Case Err.Number で番号別に分岐させると、エラーメッセージを「具体的にどう困っているのか」が伝わる文言にカスタマイズできます。利用者は次に取るべきアクションを判断しやすくなります。

Err.Clearでエラー情報をリセットする

Err.Clear メソッドを使うと、Err オブジェクトに保持されているエラー情報をリセットできます。

Sub Sample_ErrClear()
    Dim ws As Worksheet

    On Error Resume Next
    Set ws = Worksheets("シート1")
    If Err.Number <> 0 Then
        MsgBox "シート1が見つかりません"
        Err.Clear  'エラー情報をクリア
    End If

    Set ws = Worksheets("シート2")
    If Err.Number <> 0 Then
        MsgBox "シート2が見つかりません"
        Err.Clear
    End If
    On Error GoTo 0
End Sub

Err.Clear を入れておかないと、前のエラー情報が残ったまま次の処理に進みます。そのため、If Err.Number <> 0 Then の判定が誤動作する可能性があります。

NOTE

Resume Resume Next On Error GoTo などの実行時には、VBAが自動的に Err.Clear を呼び出します。明示的な Err.Clear が必要なのは、On Error Resume Next で複数の処理を試行する場合です。

ログ出力に活用する

Err オブジェクトの情報は、テキストファイルへのログ出力にも活用できます。

Sub Sample_ErrLog()
    On Error GoTo ErrHandler
    '--- 通常処理 ---
    Workbooks.Open "C:reportssales.xlsx"
    Exit Sub

ErrHandler:
    '--- ログファイルに追記 ---
    Dim logPath As String
    logPath = ThisWorkbook.Path & "error.log"

    Dim ff As Integer
    ff = FreeFile
    Open logPath For Append As #ff
    Print #ff, Format(Now, "yyyy-mm-dd hh:nn:ss") & _
               vbTab & Err.Number & vbTab & Err.Description
    Close #ff
End Sub

エラー発生日時・番号・説明をタブ区切りで追記しておくと、後から「いつ・どんなエラーが起きたか」を CSV ライクに分析できます。

実務シーン別エラーハンドリング雛形5選

ここからは、業務でVBAを書くときに遭遇しやすい5つのシーンごとに、雛形コードと注意点をまとめます。

シーン1: 開きたいファイルが存在しない

Workbooks.Open でファイルを開く処理は、ファイル不在時に実行時エラー1004を起こします。事前に Dir 関数で存在チェックする方法と、On Error で囲む方法の両方があります。

Sub Scene1_FileNotFound()
    Const filePath As String = "C:reportssales.xlsx"

    '--- ① 事前チェック:Dir関数で存在確認 ---
    If Dir(filePath) = "" Then
        MsgBox "ファイルが見つかりません: " & filePath
        Exit Sub
    End If

    '--- ② エラーハンドラ付きで開く ---
    On Error GoTo ErrHandler
    Dim wb As Workbook
    Set wb = Workbooks.Open(filePath)

    MsgBox "開きました: " & wb.Name
    wb.Close SaveChanges:=False
    Exit Sub

ErrHandler:
    MsgBox "ファイルを開けませんでした: " & Err.Description
End Sub

ポイントは、Dir で事前チェック + On Error GoTo の二重防御 です。Dir だけでは「他のユーザーがファイルを開いている」「権限が無い」場合をカバーできません。そのため、On Error も併用するのがおすすめです。

シーン2: 指定したシートが見つからない

Worksheets("名前") でシートを取得する処理は、シートが無いと実行時エラー9(添字が有効範囲にありません)を起こします。

Sub Scene2_SheetNotFound()
    Dim ws As Worksheet

    '--- Resume Next + Is Nothing判定 ---
    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets("売上データ")
    On Error GoTo 0

    If ws Is Nothing Then
        MsgBox "シート『売上データ』が存在しません"
        Exit Sub
    End If

    '--- 通常処理 ---
    ws.Range("A1").Value = "更新済み"
End Sub

On Error Resume Next で囲む範囲を Set ws = ... の1行だけに限定し、すぐに On Error GoTo 0 で解除しています。これがブロック化の基本パターンです。

シーン3: 数値変換に失敗する(型ミスマッチ)

CInt CLng CDbl などの型変換関数は、変換できない値が来ると実行時エラー13を発生させます。IsNumeric で事前判定する方法が最も読みやすくなります。

Sub Scene3_TypeMismatch()
    Dim raw As Variant
    Dim n As Long

    raw = Range("A1").Value

    '--- ① IsNumericで事前判定 ---
    If Not IsNumeric(raw) Then
        MsgBox "A1セルの値が数値ではありません: " & raw
        Exit Sub
    End If

    n = CLng(raw)
    MsgBox "変換結果: " & n
End Sub

IsNumeric は引数が数値とみなせる文字列なら True を返すため、CLng 直前のガードとして最適です。事前チェックで弾けるなら、On Error を使うよりコードが読みやすくなります。

シーン4: ループ内で1行だけスキップしたい

ループ処理中、1件だけエラーが出ても残りは最後まで処理を続けたい、という要件はよくあります。On Error Resume Next をループ内で短く囲むのがコツです。

Sub Scene4_SkipInLoop()
    Dim i As Long
    Dim lastRow As Long
    Dim n As Long

    lastRow = Cells(Rows.Count, "A").End(xlUp).Row

    For i = 2 To lastRow
        '--- 1行ごとにエラーをリセットして試行 ---
        On Error Resume Next
        n = CLng(Cells(i, "A").Value)

        If Err.Number = 0 Then
            Cells(i, "B").Value = n * 2
        Else
            Cells(i, "B").Value = "ERR"
            Err.Clear
        End If
        On Error GoTo 0
    Next i
End Sub

ポイントは、ループ反復ごとに決まった手順を踏むことです。On Error Resume Next で開始 → 試行 → Err.Number で判定 → Err.Clear でリセット → On Error GoTo 0 で解除、という流れです。エラー情報を毎回クリアしないと、次のループで誤判定が起きます。

シーン5: 外部アプリ連携(Outlook/IE)が失敗する

OutlookやWordなど、他のOfficeアプリを操作するマクロは、相手側の状態によってエラーが起きます。アプリが起動していなかったり、ライブラリが参照設定されていなかったりすると発生します。代表的なエラー番号は438(メソッド未対応)と429(オブジェクトが見つかりません)です。

Sub Scene5_ExternalApp()
    Dim olApp As Object

    On Error GoTo ErrHandler

    '--- ① 既存のOutlookを取得を試行 ---
    Set olApp = GetObject(, "Outlook.Application")

    '--- ② 通常処理 ---
    MsgBox "Outlook 取得成功: " & olApp.Name
    Exit Sub

ErrHandler:
    '--- 取得失敗時の処理 ---
    Select Case Err.Number
        Case 429
            MsgBox "Outlookが起動していません。先に起動してください。"
        Case Else
            MsgBox "予期しないエラー [" & Err.Number & "]: " & _
                   Err.Description
    End Select
End Sub

外部アプリ連携は、相手側の状態によってエラー番号が変わります。Select Case Err.Number で番号別にメッセージを出し分けると、ユーザーが次に取るべきアクションを案内できます。

On Error構文の使い分けフロー

「結局どれを使えばいいの?」という疑問に答えるため、判定軸2つで使い分けを整理します。

判定軸1: エラーで処理を止めるか継続するか

要件推奨構文理由
エラーが起きたら全体を中断したいOn Error GoTo ラベル + Exit Subエラー処理後に確実に終了
エラーが起きても残りを続行したいOn Error Resume Next(局所)スキップして次へ
エラーは絶対に起こさせたくない事前チェック(Dir / IsNumeric)そもそもエラーを発生させない

判定軸2: エラー後にどこから再開するか

エラーハンドラ内で Resume 系のステートメントを使うと、再開地点を選べます。

ステートメント再開地点用途
Exit Subプロシージャ終了エラー時に処理を中止する
Resumeエラー発生行修正後にリトライしたい
Resume Nextエラー発生行の次エラー行をスキップして続行
Resume ラベル指定ラベルクリーンアップ処理へ飛ばす

やってはいけないアンチパターン3選

最後に、現場でよく見かけるNG例を3つ紹介します。

  1. Sub の先頭に On Error Resume Next を1行だけ書く: すべてのエラーが見えなくなり、最も危険
  2. Err.Clear の呼び忘れ: ループ内で前のエラー情報が残り、次の判定が誤動作する
  3. On Error GoTo 0 の漏れ: 局所的に使ったつもりが、Sub末尾までエラー無視が続いてしまう

WARNING

On Error Resume Next は3点セットを必ず守ってください。「最小限の範囲」「直後に Err.Number 判定」「すぐに On Error GoTo 0 で解除」です。

よくある質問(FAQ)

Try-Catchはあるか?

VBAには .NET のような Try-Catch 構文はありません。代わりに3点セットで疑似的に実装します。On Error GoTo ラベル + Exit Sub + ラベル位置のエラー処理ブロック、という構成です。書き方さえ覚えてしまえば、Try-Catch と同等のことが実現できます。

Resume Nextは絶対に使ってはいけない?

そんなことはありません。On Error Resume Next は「ファイル削除を試みる(無くてもOK)」「シート存在チェック」など、失敗しても次に進めたい処理 では正当な選択肢です。重要なのは、囲む範囲を最小限にして、すぐに On Error GoTo 0 で解除することです。

ハンドリング有無で処理速度は変わる?

On Error 構文そのものは、エラーが発生しない限りパフォーマンスに大きな影響を与えません。ただし、ループ内で毎回 On Error Resume NextOn Error GoTo 0 を呼ぶ構成は、わずかにオーバーヘッドが増えます。10万行を超える大量データを扱うときは、事前チェック方式(IsNumeric など)に切り替えると体感速度が変わります。

まとめ

VBAのエラーハンドリングは、On Error GoTo On Error Resume Next Err オブジェクト の3要素で構築します。

  • On Error GoTo ラベル: エラー時に専用ブロックへジャンプ。Exit Sub とセットで使う
  • On Error Resume Next: 最小範囲で囲み、Err.Number 判定後に On Error GoTo 0 で解除する
  • Err オブジェクト: Err.Number で番号別分岐、Err.Clear でリセット、ログ出力にも使える

実務でよく遭遇する5つのシーンには、本記事の雛形コードがそのまま使えます。「ファイル不在」「シート不在」「型ミスマッチ」「ループスキップ」「外部アプリ連携」の各シーンで、コピペしてカスタマイズすれば堅牢なマクロが書けるようになります。

On Error Resume Next を1行貼り付けて凌ぐのは今日でやめてみてください。判定軸2つに沿って構文を使い分けるエラーハンドリング設計に切り替えると、次にエラーが起きたときの落ち着き具合がまったく違うはずです。

エラー番号別の対処法(13・1004・9・91・438など)は、姉妹記事のVBAマクロのエラー解決ガイド|実行時エラー13・1004・9など頻出エラー別の直し方で詳しく解説しています。あわせてご覧ください。

VBAの基本構文(変数宣言・条件分岐・LastRow取得)はそれぞれ、Excel VBAの変数の使い方Excel VBAの条件分岐(If文)の使い方Excel VBAでLastRowを取得する方法で詳しくまとめています。VBAをこれから本格的に始めたい方は、入門ハブ記事のExcel VBAでマクロ自動化を始めるための完全ガイドもぜひ参考にしてください。

タイトルとURLをコピーしました