「実行時エラー ’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-Catch の Catch ブロックに相当します。
基本構文と動作の流れ
最小構成のサンプルです。
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 0 の 0 は数字のゼロで、ラベル名ではありません。そのため、コード内に 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点セットで囲むのが鉄則です。
On Error Resume Nextでブロック開始- 試したい処理を 数行 だけ書く
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.Number | Long | エラー番号(例: 13、1004、9、91、438) |
| Err.Description | String | エラーの説明文(例: “型が一致しません”) |
| Err.Source | String | エラー発生元の名前(例: “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
ResumeResume NextOn 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つ紹介します。
- Sub の先頭に
On Error Resume Nextを1行だけ書く: すべてのエラーが見えなくなり、最も危険 Err.Clearの呼び忘れ: ループ内で前のエラー情報が残り、次の判定が誤動作する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 Next と On 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でマクロ自動化を始めるための完全ガイドもぜひ参考にしてください。
