【VBA】エラー処理(On Error)で止まらないマクロを作る方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク

この記事でできること

  • VBAマクロがエラーで途中停止しなくなる(On Error Resume Next)
  • エラーの内容(番号・説明・対象データ)をログシートに自動記録できる(On Error GoTo)
  • 「止まらない」と「原因を追跡できる」を両立した実務向けマクロが作れる

対象: Excel 2016以降 / Microsoft 365、Windows 10/11


完成イメージ(Before / After)

Before(エラー処理なし):

  • 100件のファイルを処理するマクロを実行
  • 38件目でファイル名ミスのエラーが発生
  • 「実行時エラー ’53’: ファイルが見つかりません」で停止
  • 残り62件は未処理のまま
  • 原因を直して1件目からやり直し(処理済み37件も再実行)

After(エラー処理あり):

  • 100件のファイルを処理するマクロを実行
  • 38件目でエラーが発生 → ログシートに自動記録
  • 39件目から処理を継続
  • 100件すべて完了後、「処理完了: 99件成功 / 1件エラー」と表示
  • ログシートでエラー箇所を確認し、38件目だけ手動で対応

自分も以前、100件のファイルを処理するマクロを作ったことがある。テストでは問題なく動いた。だが本番で実行したら、38件目で止まった。ファイル名が1件だけ全角スペース入りだった。残り62件は未処理。しかも処理済みの37件をもう一度やり直す羽目になった。合計1時間のロス。

On Error GoToでエラーをログに記録し、処理を続行する仕組みにしてからは、100件中1件エラーがあっても残り99件は最後まで走る。翌朝ログを見て、エラーの1件だけ手動で直せばいい。「止まらないマクロ」は、自分だけでなく同僚にも安心して渡せる。

マクロが止まるたびに「VBAは怖い」と思っている人に、この記事で「止まらないマクロ」を体験してほしい。

エラー処理は「保険」。事故が起きてから入るのではなく、最初から組み込むもの。

なお、マクロをボタンに割り当てれば毎回Alt+F8を押す手間がなくなる。方法は マクロをボタン1つで実行する方法 を参照。ファイル処理系マクロを作る前には 指定フォルダのファイルを自動バックアップする方法 でバックアップを取っておくと安心。


実行前の準備

エラー処理の2つの方式を理解する

VBAのエラー処理には大きく2つの方式がある。

方式 書き方 用途
**スキップ方式** `On Error Resume Next` 1行だけエラーを無視したいとき
**ジャンプ方式** `On Error GoTo ラベル` エラー内容を記録・分岐したいとき

この記事では、最小版で「スキップ方式」、実務版で「ジャンプ方式 + エラーログ記録」を紹介する。

Excelをマクロ有効ブック(.xlsm)で保存する

拡張子が .xlsx のままだとマクロが保存できない。

  • 「ファイル」→「名前を付けて保存」
  • ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
  • 保存

手順(コピペ → 実行まで約5分)

VBE(コードを書く画面)を開く

  • Excelで Alt + F11 を押す
  • VBE(Visual Basic Editor)が開く

標準モジュールを挿入する

  • VBEのメニュー →「挿入」→「標準モジュール」
  • 白い画面(コードウィンドウ)が表示される

コードを貼り付けて実行する

  • コードウィンドウに、下のコードをそのままコピペする
  • Alt + F8 → マクロ名を選んで「実行」

ボタンに割り当てれば毎回Alt+F8を押さなくて済む。方法は マクロをボタン1つで実行する方法 を参照。


コード(最小版)– On Error Resume Next で単一処理のエラーをスキップ


'============================================================
' ■ エラー処理(最小版)
'   → On Error Resume Next で1行だけエラーをスキップ
'   → 存在しないシートを安全に取得する例
'============================================================
Sub ErrorHandlingMinimal()

    '--- ★書き換えポイント ---
    Dim targetSheetName As String
    targetSheetName = "集計"   '← 取得したいシート名
    '--- ★ここまで ---

    Dim ws As Worksheet

    '--- エラーを一時的にスキップする
    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets(targetSheetName)
    On Error GoTo 0   '← ★必ず解除する

    '--- エラーが起きたかどうかを判定
    If ws Is Nothing Then
        MsgBox "シート「" & targetSheetName & "」が見つかりません。" & vbCrLf & _
               "シート名を確認してください。", vbExclamation
        Exit Sub
    End If

    '--- シートが見つかった場合の処理
    MsgBox "シート「" & ws.Name & "」を取得しました。" & vbCrLf & _
           "データ範囲: A1:" & ws.Cells(ws.Rows.Count, 1).End(xlUp).Address, vbInformation

End Sub

書き換えポイント

変数 説明 初期値
`targetSheetName` 取得したいシート名 `”集計”`

コードの流れ

  • On Error Resume Next でエラースキップを有効化
  • Set ws = ThisWorkbook.Worksheets(targetSheetName) でシートを取得(存在しなければエラー → スキップされて ws は Nothing のまま)
  • On Error GoTo 0 でエラースキップを必ず解除
  • If ws Is Nothing でシートが取得できたかを判定
  • 取得できなければメッセージを出して終了

重要: On Error Resume Next の後には必ず On Error GoTo 0 を書いて解除する。解除しないと、以降のすべてのエラーが無視されてしまう。この「解除忘れ」がResume Nextで一番多い事故の原因。


コード(実務版)– On Error GoTo + Err.Number + エラーログ記録

実務では「止まらない」だけでなく「なぜエラーが起きたかを後から追跡できる」ことが重要。エラーの内容をログシートに自動記録する実務版。

自分自身、以前はResume Nextをコード全体に入れて「止まらないマクロ」を作った気になっていた。だが翌月の集計で数値が合わない。調べてみると、Resume Nextでエラーが握りつぶされて、10件ほどデータが処理されていなかった。原因調査に丸1日かかった。それ以来、Resume Nextは「1行だけ」に限定し、実務では必ずOn Error GoTo + ログ記録を使うようにしている。


'============================================================
' ■ エラー処理(実務版)
'   → On Error GoTo でエラーをキャッチし、ログシートに自動記録
'   → 複数シートを一括処理し、エラーがあっても最後まで実行
'============================================================
Sub ErrorHandlingAdvanced()

    '--- ★書き換えポイント ---
    Dim logSheetName As String
    logSheetName = "ErrorLog"   '← エラーログを記録するシート名
    '--- ★ここまで ---

    '--- ログシートを準備(なければ自動作成)
    Dim wsLog As Worksheet
    On Error Resume Next
    Set wsLog = ThisWorkbook.Worksheets(logSheetName)
    On Error GoTo 0

    If wsLog Is Nothing Then
        Set wsLog = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count))
        wsLog.Name = logSheetName
        '--- ヘッダー行を作成
        wsLog.Range("A1").Value = "日時"
        wsLog.Range("B1").Value = "マクロ名"
        wsLog.Range("C1").Value = "エラー番号"
        wsLog.Range("D1").Value = "エラー内容"
        wsLog.Range("E1").Value = "対象データ"
        wsLog.Rows(1).Font.Bold = True
    End If

    '--- 処理カウンター
    Dim successCount As Long
    Dim errorCount As Long
    successCount = 0
    errorCount = 0

    '--- 全シートを一括処理(ErrorLogシートは除く)
    Dim ws As Worksheet
    For Each ws In ThisWorkbook.Worksheets

        '--- ログシート自身はスキップ
        If ws.Name = logSheetName Then GoTo NextSheet

        '--- ★ここからエラーハンドリング開始 ---
        On Error GoTo ErrHandler

        '--- シートのA列最終行を取得
        Dim lastRow As Long
        lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

        '--- B列に「処理済み」スタンプを押す(例)
        Dim i As Long
        For i = 2 To lastRow
            '--- A列の値を数値に変換してB列に記録
            ws.Cells(i, 2).Value = CDbl(ws.Cells(i, 1).Value) * 1.1
        Next i

        successCount = successCount + 1
        GoTo NextSheet

ErrHandler:
        '--- エラー情報をログシートに記録
        Dim logRow As Long
        logRow = wsLog.Cells(wsLog.Rows.Count, 1).End(xlUp).Row + 1

        wsLog.Cells(logRow, 1).Value = Now                  '日時
        wsLog.Cells(logRow, 2).Value = "ErrorHandlingAdvanced" '  マクロ名
        wsLog.Cells(logRow, 3).Value = Err.Number            'エラー番号
        wsLog.Cells(logRow, 4).Value = Err.Description       'エラー内容
        wsLog.Cells(logRow, 5).Value = "シート: " & ws.Name & " / 行: " & i  '対象データ

        errorCount = errorCount + 1
        Err.Clear
        Resume NextSheet   '← 次のシートに進む

NextSheet:
        On Error GoTo 0   '← エラー処理をリセット
    Next ws

    '--- 完了メッセージ
    Dim msg As String
    msg = "処理完了" & vbCrLf & _
          "成功: " & successCount & " シート" & vbCrLf & _
          "エラー: " & errorCount & " シート"

    If errorCount > 0 Then
        msg = msg & vbCrLf & vbCrLf & _
              "エラー詳細は「" & logSheetName & "」シートを確認してください。"
    End If

    MsgBox msg, vbInformation

End Sub

書き換えポイント

変数 説明 初期値
`logSheetName` エラーログを記録するシート名 `”ErrorLog”`

コードの流れ

  • ログシート準備: ErrorLog シートが存在しなければ自動作成し、ヘッダー行を追加
  • 全シートをループ: For Each ws In ThisWorkbook.Worksheets で全シートを処理
  • エラーハンドリング開始: On Error GoTo ErrHandler でエラー発生時にジャンプ先を指定
  • 正常処理: A列の値を1.1倍にしてB列に記録(これは例。実務に合わせて書き換える)
  • エラー時: ErrHandler ラベルにジャンプし、Err.Number / Err.Description をログシートに記録
  • 次のシートへ: Resume NextSheet で次のシートの処理に進む
  • 完了メッセージ: 成功件数とエラー件数を表示

エラーログの記録があれば、翌朝出社して「何件エラーがあったか」「どのシートのどの行か」がすぐ分かる。複数シートの一括処理については 複数シートを一括処理する方法 を参照。


よくある落とし穴5選

1. On Error Resume Next を解除し忘れて全エラーが無視される

原因: On Error Resume Next の後に On Error GoTo 0 を書き忘れると、以降のコード全体でエラーが無視される。

自分もこれでハマった。Resume Nextを1行だけのつもりで書いたが、GoTo 0を入れ忘れた。その結果、シート名のスペルミスも、セル範囲の指定ミスも、全部スルーされた。データがおかしいことに気づいたのは3日後だった。

対策: On Error Resume Next を書いたら、すぐ次の行か数行後に On Error GoTo 0 を書く。この2つは必ずセットで使う。

2. Exit Sub を忘れてエラーハンドラが正常時も実行される

原因: On Error GoTo ErrHandler の構文で、エラーハンドラのラベル(ErrHandler:)の手前に Exit Sub がないと、正常終了時もエラーハンドラのコードが実行されてしまう。

対策: 以下の構文を必ず守る。


Sub Sample()
    On Error GoTo ErrHandler
    ' 正常処理
    Exit Sub          '← ★これが必須
ErrHandler:
    ' エラー処理
End Sub

3. Resume Next の入れすぎでエラーが握りつぶされる

原因: コード全体を On Error Resume Next で囲むと、本来検知すべきエラーもすべてスキップされる。データが欠損しても気づけない。

自分の失敗談だが、以前100件のデータ処理でコード冒頭に On Error Resume Next を書いた。止まらなくなったのは良かったが、翌月の集計で数値が合わない。調べたら10件分のデータが処理されていなかった。Resume Nextでエラーがスキップされ、データが書き込まれていなかったのが原因。原因調査に丸1日かかった。

対策: Resume Nextは「1行だけ」に限定する。複数行の処理には On Error GoTo を使い、エラーをログに記録する。

4. Err.Clear を呼ばずにエラー情報が残ったまま次の処理に進む

原因: On Error Resume Next でエラーをスキップした後、Err.Number を確認せずに次の処理に進むと、前のエラー情報が残ったままになる。

対策: エラーチェック後は Err.Clear で明示的にクリアする。または On Error GoTo 0 でリセットする(GoTo 0 はErr情報もクリアされる)。

5. エラーハンドラ内でさらにエラーが発生して止まる

原因: On Error GoTo ErrHandler のエラーハンドラ内でエラーが発生すると、二重エラーで強制停止する。たとえば、ログシートへの書き込み自体がエラーになるケース。

対策: エラーハンドラ内の処理は極力シンプルにする。ログ書き込みの前に On Error Resume Next を入れる方法もあるが、エラーハンドラが複雑になるのは設計の問題。


FAQ

Q1: On Error Resume Next と On Error GoTo はどう使い分ける?

場面 方式 理由
ファイル・シートの存在確認(1行) `Resume Next` 結果をIf文で判定できる
ループ内の複数行処理 `GoTo ラベル` エラー内容を記録して続行できる
処理全体のセーフティネット `GoTo ラベル` 想定外エラーをキャッチできる

Q2: Err.Number でよく使う番号は?

番号 説明 よくある原因
9 インデックスが有効範囲にない 存在しないシート名
13 型が一致しない 文字列を数値変換
53 ファイルが見つからない パスの間違い
70 書き込みできない ファイルが開かれている
1004 アプリケーション定義エラー シート保護、範囲外

Q3: エラーログをファイルに出力したい

シートではなくテキストファイルに出力することも可能。


Dim f As Integer
f = FreeFile
Open "C:\log\error_log.txt" For Append As #f
Print #f, Now & vbTab & Err.Number & vbTab & Err.Description
Close #f

ファイル出力の基本は フォルダ内ファイル一覧を自動取得 でDir関数を使ったファイル操作を解説している。

Q4: マクロを人に渡すとき、エラー処理は必須?

必須ではないが、強く推奨する。エラー処理なしのマクロは、自分の環境でしかテストされていない。別の人のPCでは、ファイルパスが違う、シート名が違う、データ型が違うなど、想定外のエラーが頻発する。エラー処理を入れておけば「止まっても原因が分かる」マクロになる。

Q5: On Error GoTo -1 は何をするもの?

On Error GoTo -1 は、エラー処理状態をリセットする。通常は On Error GoTo 0 で十分だが、エラーハンドラ内でさらに別の On Error GoTo を使いたい場合に必要になる。初心者のうちは On Error GoTo 0 だけ覚えておけば問題ない。


まとめ

  • On Error Resume Next は「1行だけ」のエラースキップに使う(最小版)
  • On Error GoTo ラベル + Err.Number + ログ記録で「止まらない+原因追跡」を両立(実務版)
  • On Error GoTo 0 での解除を忘れると、全エラーが握りつぶされる
  • エラー処理は「保険」。マクロを人に渡す前に必ず入れる

関連記事


次にやりたくなること


エラー処理を入れたマクロは、安心して人に渡せる。まずは最小版で「止まらないマクロ」を体験してみてほしい。

あわせて読みたい

コメント

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