VBA エラー処理 On Error

VBA エラー処理 On Error アイキャッチ画像 マクロ・VBA

金曜日の夕方、退社間際に実行したマクロが「実行時エラー」で止まり、画面が黄色く光る。そんな経験はないだろうか。
せっかく自動化したはずの作業が、予期せぬデータの不備やファイルパスの変更でストップしてしまう。
この「マクロが止まる」という現象をコントロールし、スマートに回避するための技術がエラーハンドリングだ。
Excel歴15年の筆者が、数え切れないほどのデバッグ作業を経てたどり着いた、実務で本当に使えるエラー対策の真髄を伝えていく。

VBA エラー処理 On Error - マクロ実行中に「実行時エラー」のダイアログが表示された状態
マクロ実行中に「実行時エラー」のダイアログが表示された状態

実行時エラーでマクロが止まる恐怖を「VBA エラー処理 On Error」で克服する

VBAを書き始めたばかりの人が最初に直面する壁が、プログラムが途中で停止してしまうこと。
特に、自分以外の誰かが使うツールを作った際、「マクロが動かないんだけど」と言われることほど心苦しいものはない。
ここで重要な役割を果たすのが「On Error」ステートメントだ。
これは、エラーが発生したときに「次に何をすべきか」をVBAに指示するための命令である。

筆者の経験では、実務で使われるマクロの半分以上は、エラー処理が不十分なためにメンテナンスコストが増大している。
例えば、経理部の月次決算用ツールで、特定のセルに数字ではなく文字が入っていただけで処理が止まってしまう。
これでは、自動化のメリットが半減してしまうだろう。
エラー処理を組み込むことは、単にプログラムを止めないためだけではなく、ユーザーに対して「何が原因で処理が中断したのか」を正しく伝えるための優しさでもある。

ポイント: エラー処理は「失敗を隠すため」ではなく「失敗を制御して安全に処理を継続、または終了させるため」にある。

エラーを無視して進むべきか、それとも立ち止まるべきかの判断基準

エラー処理には大きく分けて、二つの考え方がある。
一つは「エラーが起きても無視して次の処理へ進む」方法。
もう一つは「エラーが起きたら専用の処理(エラーハンドラー)にジャンプする」方法だ。
実務でどちらを選ぶべきかは、その処理の重要性と、エラーの原因が予測可能かどうかにかかっている。

研修で教えていると、初心者の多くが「とりあえず On Error Resume Next(エラーを無視)」を使ってしまいがちだ。
しかし、これは非常に危険な選択になることもある。
例えば、ある製造業の営業部で、数万行の受注データを集計するマクロがあったとする。
もし単価計算でエラーが起きたのを無視して処理を続ければ、合計金額が誤ったまま報告書が作成されてしまう。
実務でよく見かけるのは、こうした「静かなエラー」が積み重なり、後で取り返しのつかないミスに繋がるケースだ。

一方で、オブジェクトの存在確認や、削除しようとしたシートが既に存在しない場合など、エラーが起きても大勢に影響がない場面では「無視」が正解になる。
この使い分けができるようになると、脱・初心者と言える。

経理の月次処理で必須となる「On Error GoTo」の基本的な記述パターン

経理部の業務のように、1円の狂いも許されない処理では、「On Error GoTo ◯◯」という形式を基本に据えるべきだ。
これは、エラーが発生した瞬間に、あらかじめ用意しておいた「エラーハンドリング用のセクション」へ処理を飛ばす書き方。
以下のコードは、筆者が社内研修で配布している標準的なテンプレートだ。

Sub MonthlyProcess()
    On Error GoTo ErrorHandler

    ' ここにメインの処理を書く
    Dim result As Double
    result = 100 / Range("A1").Value ' A1が0だとエラー発生
    
    ' 正常終了した場合は、エラー処理に入らないようにExit Subする
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました。" & vbCrLf & _
           "エラー番号:" & Err.Number & vbCrLf & _
           "内容:" & Err.Description, vbCritical, "月次処理エラー"
End Sub
VBA エラー処理 On Error - エラー発生時にメッセージボックスで詳細が表示される様子
エラー発生時にメッセージボックスで詳細が表示される様子

この構造の肝は、`Exit Sub`の存在だ。
これがないと、正常に処理が終わった後も下の`ErrorHandler`セクションまで実行されてしまう。
初心者がつまずきやすいポイントとして、この`Exit Sub`を忘れて、毎回「エラーが発生しました」と表示されてしまうミスをよく見かける。

Microsoft公式: On Error ステートメントを確認すると、エラー処理の有効範囲はプロシージャ単位であることがわかる。
つまり、一つのサブプロシージャ内でこの宣言を行えば、その中でのトラブルは全て捕捉できる。

営業部の顧客リスト集計で役立つ「On Error Resume Next」の賢い使い分け

次に、「On Error Resume Next」について深く掘り下げてみよう。
これは「エラーが起きても、その行は飛ばして次の行を実行せよ」という命令だ。
一見無責任に聞こえるが、特定のシチュエーションでは非常に強力な武器になる。

例えば、営業部で「複数の支店から送られてきたブックを順に開き、特定のシートからデータをコピーする」というマクロを想像してほしい。
中には、担当者がシート名を勝手に変えていたり、シート自体を削除していたりするブックが混ざっているかもしれない。
一つひとつの不備でマクロが止まっていては、何百というファイルを処理するのに時間がかかりすぎてしまう。

Sub CollectData()
    Dim ws As Worksheet
    On Error Resume Next ' エラーを無視開始
    
    Set ws = Worksheets("売上集計")
    
    If Err.Number <> 0 Then
        ' シートがなかった場合の処理を個別に書ける
        Debug.Print "シートが見つかりません。スキップします。"
        Err.Clear ' エラー情報をリセット
    Else
        ' シートがあった場合の処理
        Debug.Print "データをコピーします。"
    End If
    
    On Error GoTo 0 ' エラー無視を解除(重要!)
End Sub

筆者が実務でアドバイスするのは、「Resume Next を使う範囲を最小限にする」こと。
上記のように、エラーが予想される1行の直前で宣言し、直後に `On Error GoTo 0` でリセットするのがプロの鉄則だ。
これによって、予期せぬ場所でのエラーまで見逃してしまうリスクを最小限に抑えられる。

エラー発生箇所を特定するための「Errオブジェクト」の活用術

VBAには、発生したエラーの情報を保持する「Errオブジェクト」という便利な仕組みが備わっている。
エラー処理セクションに飛んできたとき、このオブジェクトを調べることで、何が起きたのかを具体的に把握できる。

主なプロパティは以下の通りだ。

  • Err.Number: エラー番号(例:0は正常、11は0除算、1004は定義エラーなど)
  • Err.Description: エラーの内容を示す説明文
  • Err.Source: エラーを発生させたオブジェクトやアプリケーションの名前

実務でよく見かけるのは、単に「エラーです」とだけ表示して終わってしまうツールだ。
しかし、大規模な社内ツールを作成する際は、これらの情報をログファイルや隠しシートに記録しておくことを勧める。
「どのユーザーが、いつ、どのファイルで、どのエラーを出したか」を自動的に記録するようにしておけば、バグ修正のスピードは格段に上がるだろう。

' ログ出力の例
Open ThisWorkbook.Path & "\error_log.txt" For Append As #1
Print #1, Now & " - Error: " & Err.Number & " / " & Err.Description
Close #1
VBA エラー処理 On Error - テキストファイルに出力されたエラーログの例
テキストファイルに出力されたエラーログの例

予期せぬトラブルを防ぐためのエラーハンドラーの出口戦略

エラーが発生した後、どのように処理を復帰させるかも腕の見せ所だ。
ただ終了するだけがエラー処理ではない。
「Resume」ステートメントを使いこなせば、エラーを修正した後に再試行させるといった高度な制御が可能になる。

  1. Resume: エラーが発生した「その行」をもう一度実行する。ユーザーにファイルを指定し直させた後に再実行する場合などに使う。
  2. Resume Next: エラーが発生した行の「次の行」から再開する。
  3. Resume [ラベル]: 指定した場所にジャンプして再開する。

ある不動産管理会社のシステムでは、共有フォルダへのアクセスエラーが頻発していた。
そこで、エラーハンドラー内で「3秒待機して再試行」というループを5回繰り返す仕組みを導入したところ、ネットワークの瞬断によるマクロ停止をゼロにすることができた。
これは、単純な `On Error Resume Next` では実現できない、戦略的なエラー処理の好例と言える。

注意点: Resume を使う際は、無限ループに陥らないよう注意が必要。必ずリトライ回数を制限するカウンターを設けよう。

部署を跨ぐツール開発で見落としがちな「エラーの連鎖」の制御

自分が作った関数やサブプロシージャを、他のマクロから呼び出す(Callする)構成にする場合、エラー処理はより複雑になる。
これを「エラーのバブリング(連鎖)」と呼ぶ。
呼び出された側(子)でエラーが起き、そこでエラー処理が行われていない場合、エラーは呼び出し元(親)へと伝わっていく。

筆者が大手メーカーの社内システムを改修した際、この「連鎖」が原因でどこでエラーが起きているか全くわからなくなっているケースに遭遇した。
理想的なのは、各プロシージャで適切なエラー処理を行うことだが、全ての関数に記述するのは現実的ではないこともある。

重要なのは、「データの整合性を担保すべき場所」で確実にトラップすること。
例えば、データベースへの書き込みを行う関数であれば、そこでエラーが起きたら必ずロールバック(処理の取り消し)を行い、親プロシージャに「失敗した」という事実を戻り値として伝えるべきだ。

VBA エラー処理 On Error - プロシージャ間のエラー伝播のイメージ図
プロシージャ間のエラー伝播のイメージ図

手動操作では気づけない「実行時エラー1004」の正体と解決策

VBAユーザーが最も多く遭遇し、かつ最も悩まされるのが「実行時エラー 1004」だろう。
「アプリケーション定義またはオブジェクト定義のエラーです」という、あまりにも抽象的な説明文。
これに泣かされた人は多いはずだ。

このエラーの正体は、実は多岐にわたる。
実務でよく見かける原因は以下のようなものだ。

  • 保護されているシートを変更しようとした
  • 存在しないセル範囲(Range)を指定した
  • 不正な文字をシート名にしようとした
  • ファイルを開く際に、同名のファイルが既に開かれていた

経理部の集計ツールでよくあるのが、「前月分のシートをコピーして新しく作ろうとしたが、既に同名のシートが存在していてコピーに失敗する」というケース。
これを回避するには、コピー前に同名シートの有無をチェックするか、あるいは以下のように一時的にエラーを無視して削除を試みる手法が有効だ。

On Error Resume Next
Application.DisplayAlerts = False ' 削除確認メッセージを非表示
Worksheets("2024年6月").Delete
Application.DisplayAlerts = True
On Error GoTo 0
VBA エラー処理 On Error - 実行時エラー1004が発生した際のデバッグ画面
実行時エラー1004が発生した際のデバッグ画面

外部ファイル連携で威力を発揮するネットワークエラーへの備え

現代の業務で、自分のPC内だけで完結するマクロは稀だ。
大抵は社内サーバーの共有フォルダにあるファイルを開いたり、Web上のAPIからデータを取得したりする。
ここで最大の敵となるのが、通信環境の不安定さだ。

筆者の経験では、こうした外部連携マクロこそ、エラー処理の「厚み」がツールの信頼性を決める。
例えば、以下のようなステップでエラーを回避することを勧める。

  1. `Dir`関数や`FileSystemObject`で、そもそもファイルが存在するか事前にチェックする。
  2. ファイルを開く処理の直前に `On Error GoTo NetworkError` を置く。
  3. ファイルが読み取り専用で開かれていないか、`ReadOnly`プロパティを確認する。

Microsoft公式: Err オブジェクトを活用し、エラー番号が「76(パスが見つかりません)」や「53(ファイルが見つかりません)」の場合、ユーザーにネットワーク接続の確認を促すメッセージを出すのが親切だ。

メンテナンス性を高めるための標準的なエラー処理テンプレートの構築

マクロの規模が大きくなればなるほど、場当たり的なエラー処理はコードの可読性を下げる。
そこで、チームや部署で「エラー処理の型」を決めておくことが、長期的なメンテナンス性を高めるコツだ。

筆者が提唱しているのは、各プロシージャの末尾に「後処理(Cleanup)」セクションを作ること。
エラーが起きても起きなくても、必ず通過する場所を用意しておくのだ。
これによって、エラー時に開きっぱなしになったファイルを閉じたり、停止させた画面更新(ScreenUpdating)を再開したりする処理を漏れなく記述できる。

Sub ProfessionalStructure()
    On Error GoTo ErrHandler
    Application.ScreenUpdating = False
    
    ' メイン処理
    
CleanExit:
    ' 正常でもエラーでも必ず実行したい処理
    Application.ScreenUpdating = True
    Exit Sub

ErrHandler:
    MsgBox "エラー番号: " & Err.Number
    Resume CleanExit
End Sub

この構成なら、どこでエラーが起きても確実に `ScreenUpdating = True` が実行される。
「マクロがエラーで止まった後、Excelの画面が動かなくなった(更新されなくなった)」という、初心者がよく陥るトラブルを未然に防ぐことができる。

VBA エラー処理 On Error - 構造化されたVBAコードの全体像
構造化されたVBAコードの全体像

10年後も動くコードにするための例外処理の設計思想

マクロは一度作って終わりではない。
Excelのバージョンが上がり、OSが変わり、会社のサーバー構成が変わる。
そうした変化に耐えうるコードには、優れた例外処理が組み込まれている。

重要なのは、「防げるエラーは、On Error に頼らず事前に防ぐ」という思想だ。
これを「エラー回避」と呼ぶ。
例えば、0で割る可能性があるなら、`If Range(“A1”).Value <> 0 Then` と条件分岐で書くべきであり、`On Error` で無理やり突破すべきではない。

実務で15年以上Excelを見てきて確信しているのは、`On Error` はあくまで「どうしても予測できない事態(サーバーのダウン、ユーザーによる予期せぬファイル削除など)」に対する最後の砦として使うべきだということだ。
何でもかんでもエラー処理に放り込むのは、プロの仕事とは言えない。

ここで、知らないと損する独自のTipsを5つ紹介しよう。

実務家が教えるエラー処理の秘訣:

  1. Debug.Assert を活用せよ: 開発段階では `Debug.Assert 条件` を使い、特定の条件を満たさない時に自動でデバッグモードに移行させる。
  2. 行番号を表示する: あまり知られていないが、VBAの行頭に数字を書くと、`Err.Line`(正確にはErl関数)で行番号を取得できる。
  3. ユーザー定義エラーを作る: `Err.Raise` を使えば、独自のルールに反した場合に意図的にエラーを発生させ、エラーハンドラーに飛ばせる。
  4. 一時停止の MsgBox を活用: エラーハンドラー内で `vbAbortRetryIgnore` を指定すれば、ユーザーに「中止・再試行・無視」を選択させることができる。
  5. VBEの設定を確認: [ツール]→[オプション]→[全般]タブの「エラー発生時に中断」が「エラー トラップのみ中断」になっているか確認。ここが「エラー発生時に中断」だと、On Error を書いていても止まってしまう。

自動化の質を高めるための実務家としての最終ステップ

エラー処理を極めることは、VBAのスキルそのものを一段上のレベルに引き上げることに他ならない。
これまでは「動くものを作る」ことに必死だったかもしれないが、これからは「止まらないものを作る」、あるいは「スマートに止まるものを作る」ことに注力してみてほしい。

経理部の月次締めや営業部の受注管理など、止まることが許されない現場で、あなたの作ったツールが安定して動き続ける。
その信頼性こそが、組織内でのあなたの評価を形作るはずだ。
まずは、今あるマクロに一つ、`On Error GoTo` を追加することから始めてみよう。
その一歩が、後であなた自身の時間を救うことになるだろう。

筆者が講師を務める研修では、最後によくこう伝えている。
「最高のコードとは、エラーが起きないコードではない。エラーが起きたときに、最もユーザーを迷わせないコードだ」と。
この視点を持って、明日からの実務に取り組んでいただければと思う。

  • まずは小規模なプロシージャで `On Error Resume Next` の挙動を確認する
  • Err.Number と Err.Description をセットで表示する癖をつける
  • 正常終了時の `Exit Sub` を忘れていないかチェックする

コメント

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