Excelマクロを実行したとき、「実行時エラー ’91’: オブジェクト変数またはWithブロック変数が設定されていません」というメッセージが表示されて処理が止まってしまう――このエラーはVBAでオブジェクトを扱うコードを書き始めた人がほぼ必ずぶつかる壁です。原因は一言で言えば「Setで中身を入れていないオブジェクト変数を使おうとした」ことですが、発生パターンは多岐にわたり、検索結果だけでは対処できないケースも多くあります。本記事では、エラー91が発生するすべてのパターンを場面ごとに整理し、その場でコピーして使える修正コードと合わせて徹底解説します。


目次


実行時エラー’91’とは何か――Nothingとオブジェクト変数の基礎

エラー91を理解するには、まずVBAにおけるオブジェクト変数と Nothing の概念を押さえる必要があります。

VBAの変数には「値型」と「参照型(オブジェクト型)」の2種類があります。LongString などの値型変数は宣言した時点でデフォルト値(0や空文字列)が入りますが、WorksheetRange などのオブジェクト型変数は宣言しただけでは何も入っておらず、Nothing という空の状態になっています。

' オブジェクト変数を宣言した直後は Nothing(何も指していない)
Dim ws  As Worksheet   ' → ws は Nothing
Dim rng As Range       ' → rng は Nothing

' Nothingのままプロパティにアクセスするとエラー91が発生する
Debug.Print ws.Name    ' → 実行時エラー '91'

オブジェクト変数を使えるようにするには、Set キーワードを使って実際のオブジェクトを代入する必要があります。このSetによる代入を「オブジェクト参照のセット」と呼びます。エラー91は「Setが実行されないまま、またはSetが失敗したままオブジェクト変数を使おうとしたとき」に発生します。

' Setで実際のオブジェクトを代入してはじめて使える
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)   ' ← これが必要

Debug.Print ws.Name   ' → "Sheet1" などが表示される(正常)

' 使い終わったらNothingで解放するのが作法
Set ws = Nothing

エラー91が発生するパターンは大きく分けて次の3つです。

  • Setを忘れた:宣言しただけで Set を書いていない
  • Setが成功しなかった:条件分岐の中でSetが実行されなかった、Findが失敗した、など
  • すでにNothingに戻ったSet obj = Nothing で解放した後に使おうとした

原因1:Setを使わずにオブジェクト変数に代入した

オブジェクト変数への代入に Set を書き忘れると、VBAは値型の代入として解釈しようとしてエラー13(型が一致しません)になる場合と、そのままNothingのまま進んでエラー91になる場合があります。いずれにせよ Set の書き忘れはエラー91の直接原因になります。

エラーが起きるコード例

' Setを忘れるとエラー(13または91)
Dim ws  As Worksheet
Dim rng As Range
Dim wb  As Workbook

ws  = ThisWorkbook.Sheets(1)     ' → エラー13(Setが必要)
rng = ActiveSheet.Range("A1")    ' → エラー13(Setが必要)
wb  = ThisWorkbook               ' → エラー13(Setが必要)

' Set自体は書いたが、代入先のオブジェクトが存在しない場合はNothingのまま
Dim col As Collection
Set col = Nothing   ' 明示的にNothingを代入
col.Add "item"      ' → 実行時エラー '91'

修正後のコード

' 必ずSetキーワードを使って代入する
Dim ws  As Worksheet
Dim rng As Range
Dim wb  As Workbook

Set ws  = ThisWorkbook.Sheets(1)
Set rng = ActiveSheet.Range("A1:A10")
Set wb  = ThisWorkbook

' オブジェクトを使う前に必ずNothingでないことを確認する
If Not ws Is Nothing Then
    Debug.Print ws.Name
End If

If Not rng Is Nothing Then
    rng.Interior.Color = RGB(255, 255, 200)
End If

' 処理後は解放する
Set ws  = Nothing
Set rng = Nothing
Set wb  = Nothing

Setが必要なオブジェクト型・不要な値型の早見表

  • Set が必要(オブジェクト型):Workbook、Worksheet、Range、Chart、Shape、PivotTable、ListObject、Collection、Dictionary、FileSystemObject、ADODB系オブジェクト、自作クラスのインスタンスなど
  • Set が不要(値型):Long、Integer、Double、Single、String、Boolean、Date、Byte、Currency、Variant(値を直接格納する場合)

迷ったときは TypeName(変数) を実行して確認するか、VBAエディタで変数名にカーソルを置いてオブジェクト型かどうかをポップアップで確認してください。


原因2:Nothingのままオブジェクトのプロパティ・メソッドを呼び出した

条件分岐や関数の戻り値によって、Set が特定の条件のときだけ実行されるコードでよく発生します。「ほとんどの場合は動くが、たまにエラーになる」という状況の原因の多くがこれです。

エラーが起きるコード例

' 条件によってはSetが実行されずNothingのまま進む
Dim ws As Worksheet

If ActiveSheet.Name = "集計" Then
    Set ws = ActiveSheet
End If

' 上のIfに入らなかった場合、wsはNothingのまま
ws.Cells(1, 1).Value = "処理済"   ' → 実行時エラー '91'
' ループ内でSetが失敗した場合
Dim targetWS As Worksheet
Dim sheetName As String
sheetName = "存在しないシート"

On Error Resume Next
Set targetWS = ThisWorkbook.Sheets(sheetName)
On Error GoTo 0

' エラーが出てもコードは続行されるが、targetWSはNothingのまま
targetWS.Activate   ' → 実行時エラー '91'

修正後のコード(使う前に必ずNothingチェック)

' ========================================
' Nothingチェックを必ず入れるパターン
' ========================================
Sub SafeObjectAccess()

    Dim ws As Worksheet

    If ActiveSheet.Name = "集計" Then
        Set ws = ActiveSheet
    End If

    ' 使う前にNothingチェック
    If ws Is Nothing Then
        MsgBox "シート「集計」がアクティブでないため処理を中断します。", vbExclamation
        Exit Sub
    End If

    ws.Cells(1, 1).Value = "処理済"
    Set ws = Nothing

End Sub
' ========================================
' On Error Resume Nextを使ったSetとNothingチェックの標準パターン
' ========================================
Sub SafeSheetAccess()

    Dim targetWS  As Worksheet
    Dim sheetName As String
    sheetName = "集計シート"

    ' Setを試みる(失敗してもエラーで止まらない)
    On Error Resume Next
    Set targetWS = ThisWorkbook.Sheets(sheetName)
    On Error GoTo 0

    ' Setが成功したか確認
    If targetWS Is Nothing Then
        MsgBox "シート「" & sheetName & "」が見つかりませんでした。" & vbCrLf & _
               "シート名を確認してください。", vbCritical
        Exit Sub
    End If

    ' 安全に処理
    targetWS.Cells(1, 1).Value = "OK"
    Set targetWS = Nothing

End Sub

原因3:FindメソッドがNothingを返しているのに使おうとした

エラー91の原因として非常に多いのが、RangeオブジェクトのFindメソッドの戻り値チェック漏れです。Findは検索値が見つかった場合にRangeオブジェクトを、見つからなかった場合に Nothing を返します。このNothingチェックを忘れると、見つからない検索値が渡ったとたんにエラー91が発生します。

エラーが起きるコード例

' Findの戻り値をチェックせずに使うとエラー91
Dim foundCell As Range
Set foundCell = ActiveSheet.Range("A:A").Find(What:="検索値")

' foundCellがNothingの場合(見つからなかった場合)はエラー91
Debug.Print foundCell.Address    ' → 実行時エラー '91'
foundCell.Interior.Color = vbYellow   ' → 実行時エラー '91'

修正後のコード(Findの戻り値を必ずチェック)

' ========================================
' FindメソッドのNothingチェック標準パターン
' ========================================
Sub SafeFind()

    Dim ws        As Worksheet
    Dim searchRange As Range
    Dim foundCell As Range
    Dim keyword   As String

    Set ws          = ThisWorkbook.Sheets(1)
    Set searchRange = ws.Range("A:A")
    keyword         = "検索値"

    Set foundCell = searchRange.Find( _
                        What:=keyword, _
                        LookIn:=xlValues, _
                        LookAt:=xlWhole, _
                        MatchCase:=False)

    ' 必ずNothingチェックを行う
    If foundCell Is Nothing Then
        MsgBox "「" & keyword & "」は見つかりませんでした。", vbInformation
    Else
        MsgBox "「" & keyword & "」は " & foundCell.Address & " にあります。", vbInformation
        foundCell.Interior.Color = vbYellow
    End If

    Set foundCell   = Nothing
    Set searchRange = Nothing
    Set ws          = Nothing

End Sub

FindNextを使ったループでのエラー91対策

' ========================================
' FindNextで複数セルを検索するときの安全なパターン
' ========================================
Sub SafeFindAll()

    Dim ws          As Worksheet
    Dim searchRange As Range
    Dim foundCell   As Range
    Dim firstAddr   As String
    Dim keyword     As String
    Dim count       As Long

    Set ws          = ThisWorkbook.Sheets(1)
    Set searchRange = ws.UsedRange
    keyword         = "対象"
    count           = 0

    ' 最初のFind
    Set foundCell = searchRange.Find( _
                        What:=keyword, _
                        LookIn:=xlValues, _
                        LookAt:=xlPart)

    ' 最初のFindがNothingなら即終了
    If foundCell Is Nothing Then
        MsgBox "「" & keyword & "」は見つかりませんでした。", vbInformation
        GoTo CleanUp
    End If

    ' 最初に見つかったアドレスを記録(ループ終了判定に使う)
    firstAddr = foundCell.Address

    Do
        count = count + 1
        foundCell.Interior.Color = vbYellow
        Debug.Print count & "件目: " & foundCell.Address

        ' 次を検索
        Set foundCell = searchRange.FindNext(foundCell)

        ' FindNextがNothingを返した場合の保険
        If foundCell Is Nothing Then Exit Do

    Loop While foundCell.Address <> firstAddr   ' 最初のセルに戻ったら終了

    MsgBox count & " 件見つかりました。", vbInformation

CleanUp:
    Set foundCell   = Nothing
    Set searchRange = Nothing
    Set ws          = Nothing

End Sub

原因4:WithブロックのオブジェクトがNothingになっている

Withブロックを使って複数のプロパティを設定するとき、WithキーワードにNothingのオブジェクトを渡すとエラー91が発生します。また、Withブロックの内側で誤って対象オブジェクトをNothingに設定してしまう場合も同様です。

エラーが起きるコード例

' wsがNothingの状態でWithに渡すとエラー91
Dim ws As Worksheet   ' Nothingのまま

With ws   ' → 実行時エラー '91'
    .Cells(1, 1).Value = "テスト"
    .Range("A1").Font.Bold = True
End With
' WithブロックのオブジェクトをNothingにして内部でアクセスするとエラー91
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)

With ws
    .Cells(1, 1).Value = "開始"
    Set ws = Nothing          ' ← Withの途中でNothingにしてはいけない
    .Cells(2, 1).Value = "終了"  ' → 実行時エラー '91'
End With

修正後のコード

' Withに渡す前に必ずNothingチェックを行う
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)

' Withに入る前のガード処理
If ws Is Nothing Then
    MsgBox "シートの取得に失敗しました。", vbCritical
    Exit Sub
End If

With ws
    .Cells(1, 1).Value    = "テスト"
    .Range("A1").Font.Bold = True
    .Range("A1").Interior.Color = RGB(200, 230, 200)
End With

' WithブロックをEnd Withで抜けてから解放する
Set ws = Nothing
' ========================================
' ネストしたWithブロックを安全に使うパターン
' ========================================
Sub SafeNestedWith()

    Dim ws  As Worksheet
    Dim rng As Range

    Set ws  = ThisWorkbook.Sheets(1)
    Set rng = ws.Range("A1:C5")

    If ws Is Nothing Or rng Is Nothing Then
        MsgBox "オブジェクトの取得に失敗しました。", vbCritical
        GoTo CleanUp
    End If

    With rng
        .Value              = "初期値"
        .Font.Name          = "メイリオ"
        .Font.Size          = 11

        With .Interior
            .Color    = RGB(240, 248, 255)
            .Pattern  = xlSolid
        End With

        With .Borders(xlEdgeBottom)
            .LineStyle = xlContinuous
            .Weight    = xlThin
        End With
    End With

CleanUp:
    Set rng = Nothing
    Set ws  = Nothing

End Sub

原因5:Setで解放した後にオブジェクトを使おうとした

Set obj = Nothing でオブジェクトを解放した後に、そのオブジェクト変数を使おうとするとエラー91が発生します。複数のプロシージャにまたがってオブジェクトを受け渡しているときや、エラーハンドラ内でCleanUpを行った後に誤ってオブジェクトを使おうとするケースで起きやすいです。

エラーが起きるコード例

' 解放した後にアクセスするとエラー91
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)

ws.Cells(1, 1).Value = "処理中"
Set ws = Nothing   ' ここで解放

' 解放後に再びアクセスしようとするとエラー91
ws.Cells(1, 2).Value = "完了"   ' → 実行時エラー '91'
' エラーハンドラでNothingにした後、処理に戻ろうとするケース
Sub ProblematicSub()

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets(1)

    On Error GoTo ErrorHandler

    ws.Cells(1, 1).Value = 1 / 0   ' ゼロ除算でエラー

    Set ws = Nothing
    Exit Sub

ErrorHandler:
    Set ws = Nothing   ' CleanUp
    Resume Next        ' 次の行に戻る → wsはNothingなのでエラー91の可能性
    ' ↑ Resume Nextは慎重に使う必要がある

End Sub

修正後のコード

' 解放前に処理をすべて完了させる
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)

' すべての処理を行ってから解放
ws.Cells(1, 1).Value = "処理中"
ws.Cells(1, 2).Value = "完了"
ws.Range("A1:B1").Font.Bold = True

Set ws = Nothing   ' 最後にまとめて解放
' ========================================
' CleanUpラベルを使った安全な解放パターン
' ========================================
Sub SafeReleasePattern()

    Dim ws  As Worksheet
    Dim rng As Range

    On Error GoTo ErrorHandler

    Set ws  = ThisWorkbook.Sheets(1)
    Set rng = ws.Range("A1:A10")

    ' ===== メイン処理 =====
    rng.Value = "処理済"
    rng.Interior.Color = RGB(200, 255, 200)
    ' =====================

    MsgBox "処理完了", vbInformation
    GoTo CleanUp

ErrorHandler:
    MsgBox "エラーが発生しました。" & vbCrLf & _
           "番号: " & Err.Number & vbCrLf & _
           "内容: " & Err.Description, vbCritical

CleanUp:
    ' エラーの有無にかかわらず必ずここで解放する
    ' Nothingの状態で Set Nothing してもエラーにならないので安全
    Set rng = Nothing
    Set ws  = Nothing

End Sub

原因6:関数がNothingを返しているのにチェックなしで使った

オブジェクトを返す関数(Function)では、処理が失敗した場合に Nothing を返すことがあります。呼び出し側でNothingチェックをしないまま戻り値を使うとエラー91が発生します。

エラーが起きるコード例

' シートを検索して返す関数
Function FindSheetByName(name As String) As Worksheet
    On Error Resume Next
    Set FindSheetByName = ThisWorkbook.Sheets(name)
    On Error GoTo 0
    ' シートが存在しない場合、Nothingが返る
End Function

' 呼び出し側でNothingチェックをしていないとエラー91
Sub CallerSub()
    Dim ws As Worksheet
    Set ws = FindSheetByName("存在しないシート")

    ' wsがNothingの状態でアクセスするとエラー91
    ws.Cells(1, 1).Value = "テスト"   ' → 実行時エラー '91'
End Sub

修正後のコード(呼び出し側と関数側の両方で対策)

' ========================================
' Nothingを返す可能性がある関数の標準的な実装と呼び出しパターン
' ========================================

' 関数側:Nothingを返す可能性を明示するコメントを書く
' 戻り値がNothingのときは呼び出し側でチェックが必要
Function FindSheetByName(name As String) As Worksheet
    ' 見つからない場合はNothingを返す(エラーは発生しない)
    On Error Resume Next
    Set FindSheetByName = ThisWorkbook.Sheets(name)
    On Error GoTo 0
End Function

' 範囲検索してRangeを返す関数(見つからない場合Nothing)
Function FindRangeByValue(ws As Worksheet, _
                           searchVal As String, _
                           Optional searchCol As Long = 1) As Range
    If ws Is Nothing Then Exit Function   ' 引数もNothingチェック

    Set FindRangeByValue = ws.Columns(searchCol).Find( _
                               What:=searchVal, _
                               LookIn:=xlValues, _
                               LookAt:=xlWhole)
    ' 見つからない場合はNothingが返る
End Function

' 呼び出し側:戻り値がNothingでないことを確認してから使う
Sub SafeCallerSub()

    Dim ws  As Worksheet
    Dim rng As Range

    Set ws = FindSheetByName("データ")

    ' 関数の戻り値は必ずNothingチェック
    If ws Is Nothing Then
        MsgBox "「データ」シートが見つかりません。", vbCritical
        Exit Sub
    End If

    Set rng = FindRangeByValue(ws, "山田太郎")

    If rng Is Nothing Then
        MsgBox "「山田太郎」は見つかりませんでした。", vbInformation
    Else
        MsgBox "「山田太郎」は " & rng.Address & " にあります。", vbInformation
        rng.Interior.Color = vbCyan
    End If

CleanUp:
    Set rng = Nothing
    Set ws  = Nothing

End Sub

原因7:イベントプロシージャのオブジェクト引数がNothing

ボタンクリックイベントや、SelectionChange などのシートイベントで渡されるオブジェクト引数が、まれにNothingになっているケースがあります。また、イベントプロシージャ内でモジュールレベルの変数をオブジェクトとして使うとき、初期化されていない場合にエラー91が発生します。

エラーが起きるコード例

' モジュールレベルのオブジェクト変数が未初期化のまま使われる
Dim g_ws As Worksheet   ' モジュールレベルで宣言

' ボタンクリックイベント
Private Sub CommandButton1_Click()
    ' g_wsが初期化されていないとエラー91
    g_ws.Cells(1, 1).Value = "クリックされました"   ' → エラー91
End Sub

修正後のコード

' モジュールレベル変数は使う前に必ず初期化する
Dim g_ws As Worksheet

' ブックを開いたとき(または初回ボタンクリック時)に初期化
Private Sub Workbook_Open()
    Set g_ws = ThisWorkbook.Sheets("ログ")
End Sub

Private Sub CommandButton1_Click()
    ' 念のためNothingチェックを入れる
    If g_ws Is Nothing Then
        Set g_ws = ThisWorkbook.Sheets("ログ")
    End If

    If g_ws Is Nothing Then
        MsgBox "「ログ」シートが見つかりません。", vbCritical
        Exit Sub
    End If

    g_ws.Cells(g_ws.Rows.Count, 1).End(xlUp).Offset(1, 0).Value = _
        Format(Now(), "yyyy/mm/dd HH:mm:ss") & " クリックされました"
End Sub
' SelectionChangeイベントでの安全なオブジェクト操作
Private Sub Worksheet_SelectionChange(ByVal Target As Range)

    ' Targetは通常Nothingにならないが念のためチェック
    If Target Is Nothing Then Exit Sub

    ' 選択が複数セルにまたがる場合の処理
    If Target.Cells.Count > 1 Then Exit Sub

    ' 特定列(A列)が選択されたときだけ処理
    If Target.Column <> 1 Then Exit Sub

    ' 安全に処理
    Application.EnableEvents = False
    Target.Interior.Color = RGB(255, 255, 200)
    Application.EnableEvents = True

End Sub

Nothing判定ユーティリティ関数集

エラー91対策として汎用的に使えるユーティリティ関数をまとめました。標準モジュールに追加しておくとプロジェクト全体で再利用できます。

' ========================================
' エラー91対策 Nothing判定ユーティリティ集
' 標準モジュールに貼り付けて使用
' ========================================

' ----- オブジェクトがNothingでないか確認 -----
Function IsSet(obj As Object) As Boolean
    IsSet = Not (obj Is Nothing)
End Function

' ----- シートの存在確認とオブジェクト取得 -----
Function GetSheetSafe(sheetName As String, _
                      Optional wb As Workbook) As Worksheet
    If wb Is Nothing Then Set wb = ThisWorkbook
    On Error Resume Next
    Set GetSheetSafe = wb.Sheets(sheetName)
    On Error GoTo 0
    ' 戻り値がNothingの場合、呼び出し側でチェックが必要
End Function

' ----- 範囲の安全な取得 -----
Function GetRangeSafe(ws As Worksheet, addr As String) As Range
    If ws Is Nothing Then Exit Function
    On Error Resume Next
    Set GetRangeSafe = ws.Range(addr)
    On Error GoTo 0
End Function

' ----- Findの安全なラッパー -----
Function FindCellSafe(searchRange As Range, _
                      keyword As String, _
                      Optional lookAt As XlLookAt = xlWhole, _
                      Optional matchCase As Boolean = False) As Range
    If searchRange Is Nothing Then Exit Function
    Set FindCellSafe = searchRange.Find( _
                           What:=keyword, _
                           LookIn:=xlValues, _
                           LookAt:=lookAt, _
                           MatchCase:=matchCase)
    ' 見つからない場合はNothingを返す
End Function

' ----- ブックが開かれているか確認して取得 -----
Function GetOpenWorkbook(wbName As String) As Workbook
    On Error Resume Next
    Set GetOpenWorkbook = Workbooks(wbName)
    On Error GoTo 0
End Function

' ----- 複数オブジェクトをまとめて解放 -----
Sub ReleaseObjects(ParamArray objs() As Variant)
    Dim i As Long
    For i = LBound(objs) To UBound(objs)
        If IsObject(objs(i)) Then
            If Not objs(i) Is Nothing Then
                Set objs(i) = Nothing
            End If
        End If
    Next i
End Sub

' ========================================
' 使用例
' ========================================
Sub UtilityUsageExample()

    Dim ws   As Worksheet
    Dim rng  As Range
    Dim cell As Range

    ' シートを安全に取得
    Set ws = GetSheetSafe("データ入力")
    If Not IsSet(ws) Then
        MsgBox "「データ入力」シートが見つかりません。", vbCritical
        Exit Sub
    End If

    ' 範囲を安全に取得
    Set rng = GetRangeSafe(ws, "A1:A100")
    If Not IsSet(rng) Then
        MsgBox "範囲の取得に失敗しました。", vbCritical
        GoTo CleanUp
    End If

    ' Findを安全に実行
    Set cell = FindCellSafe(rng, "山田太郎")
    If Not IsSet(cell) Then
        MsgBox "「山田太郎」は見つかりませんでした。", vbInformation
        GoTo CleanUp
    End If

    cell.Interior.Color = vbYellow
    MsgBox "見つかりました: " & cell.Address, vbInformation

CleanUp:
    Set cell = Nothing
    Set rng  = Nothing
    Set ws   = Nothing

End Sub

デバッグ手順:どの変数がNothingかを特定する

エラー91が発生したとき、どのオブジェクト変数がNothingになっているかを素早く特定する方法です。

ステップ1:エラー行を確認してオブジェクト変数を特定する

エラーダイアログの「デバッグ」ボタンを押すと、VBAエディタでエラー行が黄色くハイライトされます。その行でどのオブジェクト変数を使っているかを確認してください。

ステップ2:イミディエイトウィンドウでNothingかどうかを確認する

' デバッグモード中にイミディエイトウィンドウ(Ctrl+G)で以下を入力
' ?(ws Is Nothing)  → Trueならwsはなにも設定されていない
' ?(rng Is Nothing) → Trueならrngはなにも設定されていない
' ?TypeName(ws)       → "Nothing"と表示されれば未設定

ステップ3:各変数の状態を一括チェックするデバッグコードを挿入する

' エラーが起きる処理の直前に以下のデバッグコードを一時的に挿入する
Sub DebugObjectStatus()

    Dim ws   As Worksheet
    Dim rng  As Range
    Dim cell As Range

    ' ... (Setの処理) ...

    ' オブジェクトの状態を一括確認
    Debug.Print "=== オブジェクト変数の状態確認 ==="
    Debug.Print "ws   : " & IIf(ws   Is Nothing, "Nothing(未設定)", TypeName(ws)   & " - " & ws.Name)
    Debug.Print "rng  : " & IIf(rng  Is Nothing, "Nothing(未設定)", TypeName(rng)  & " - " & rng.Address)
    Debug.Print "cell : " & IIf(cell Is Nothing, "Nothing(未設定)", TypeName(cell) & " - " & cell.Address)
    Debug.Print "=================================="

End Sub

ステップ4:F8ステップ実行でSetが実行されているかを追う

VBAエディタでF8キーを押すと1行ずつ実行するステップ実行モードになります。Setキーワードのある行を通過したあと、イミディエイトウィンドウで ? TypeName(変数名) と入力してオブジェクトが正常に設定されたかを確認してください。Setが条件分岐の中にある場合、その分岐を通過したかどうかも確認します。


エラー91対応エラーハンドリングテンプレート

' ========================================
' エラー91に対応したエラーハンドリングテンプレート
' ========================================
Sub MainProcess()

    Dim ws1  As Worksheet
    Dim ws2  As Worksheet
    Dim rng  As Range
    Dim cell As Range

    Application.ScreenUpdating = False
    Application.Calculation    = xlCalculationManual
    Application.EnableEvents   = False

    On Error GoTo ErrorHandler

    ' オブジェクトの取得
    Set ws1 = GetSheetSafe("データ入力")
    Set ws2 = GetSheetSafe("集計")

    ' 取得後のNothingチェック
    If ws1 Is Nothing Then
        Err.Raise 91, "MainProcess", "「データ入力」シートが見つかりません。"
    End If
    If ws2 Is Nothing Then
        Err.Raise 91, "MainProcess", "「集計」シートが見つかりません。"
    End If

    Set rng = ws1.Range("A2:A100")

    ' ===== メイン処理 =====
    Set cell = FindCellSafe(rng, "処理対象")
    If Not cell Is Nothing Then
        ws2.Cells(1, 1).Value = cell.Value
    End If
    ' =====================

    MsgBox "処理完了", vbInformation
    GoTo CleanUp

ErrorHandler:
    Dim msg As String

    Select Case Err.Number
        Case 91
            msg = "オブジェクト変数が設定されていません(エラー91)。" & vbCrLf & vbCrLf & _
                  "確認してください:" & vbCrLf & _
                  "・Setキーワードを使ってオブジェクトを代入しているか" & vbCrLf & _
                  "・Findや関数の戻り値がNothingでないか確認しているか" & vbCrLf & _
                  "・解放済み(Nothing)のオブジェクトを使っていないか"
        Case 9
            msg = "シート・配列の添字が有効範囲にありません(エラー9)。"
        Case 1004
            msg = "Excelオブジェクトへの操作が失敗しました(エラー1004)。"
        Case Else
            msg = "予期しないエラーが発生しました。"
    End Select

    MsgBox msg & vbCrLf & vbCrLf & _
           "エラー番号:" & Err.Number & vbCrLf & _
           "内容:" & Err.Description & vbCrLf & _
           "発生元:" & Err.Source, _
           vbCritical, "実行時エラー " & Err.Number

CleanUp:
    Application.ScreenUpdating = True
    Application.Calculation    = xlCalculationAutomatic
    Application.EnableEvents   = True
    Application.CutCopyMode    = False

    Set cell = Nothing
    Set rng  = Nothing
    Set ws2  = Nothing
    Set ws1  = Nothing

End Sub

エラー91 原因チェックリスト

エラー91が発生したとき、以下のリストを上から順番に確認してください。

  • オブジェクト変数への代入に Set キーワードを使っているか
  • 値型変数(Long・Stringなど)に誤って Set を使っていない
  • オブジェクト変数を使う前に If obj Is Nothing Then でNothingチェックをしているか
  • 条件分岐や例外処理の中でSetが実行されない経路がないか(F8ステップ実行で確認)
  • Findメソッドの戻り値をNothingチェックなしに使っていないか
  • FindNextループで最初のFindがNothingを返した場合の処理があるか
  • Withブロックに渡すオブジェクトが Withの開始前にNothingでないことを確認しているか
  • WithブロックのEnd Withに到達する前に 対象オブジェクトをNothingにしていない
  • Set obj = Nothing で解放した後に、同じ変数を再使用しようとしていない
  • オブジェクトを返す関数の戻り値が Nothing を返す可能性があり、呼び出し側でNothingチェックをしているか
  • モジュールレベルのオブジェクト変数が使用前に必ず初期化されているか(Workbook_Openイベントで初期化するなど)
  • イミディエイトウィンドウで ? TypeName(変数名) を実行して “Nothing” と表示されていないか確認したか

まとめ

実行時エラー’91’「オブジェクト変数またはWithブロック変数が設定されていません」は、Nothingのままのオブジェクト変数を使おうとしたときに発生します。原因は必ずSetによる代入の失敗・漏れ・解放後の使用のいずれかです。本記事で解説した内容の要点は次のとおりです。

  • Setの書き忘れ:オブジェクト変数への代入には必ず Set を使う。迷ったら TypeName で型を確認する
  • Nothingチェック:Setの後は必ず If obj Is Nothing Then で確認してから使う
  • Findの戻り値:Find・FindNextの結果は必ずNothingチェックを行う。FindNextループでは最初のFindがNothingの場合の処理も必須
  • Withブロック:Withに渡す前にNothingチェックし、Withの内側でオブジェクトをNothingにしない
  • 解放後の使用Set obj = Nothing 後に同変数を使わない。CleanUpラベルパターンでまとめて解放する
  • 関数の戻り値:オブジェクトを返す関数の戻り値は必ず呼び出し側でNothingチェックを行う
  • デバッグ:イミディエイトウィンドウで ? TypeName(変数名) を実行するのが最速の原因特定手段

本記事で紹介したユーティリティ関数集(GetSheetSafe・GetRangeSafe・FindCellSafe・IsSet)を標準モジュールに一度追加しておけば、以後のすべてのマクロでエラー91への耐性が大幅に向上します。「オブジェクトを使う前に必ずNothingチェックをする」という習慣がエラー91を根絶する唯一の方法です。