VBAの「実行時エラー9: インデックスが有効範囲にありません」は、コードが存在しない要素を参照したときに起きます。Excelではシート名やブック名のずれ、配列の添字範囲外、動的配列の初期化前アクセスなどが原因になります。止まった行を見れば切り分けやすいので、参照対象ごとに確認していきましょう。
まず確認:エラー9は「存在しない要素を参照した」ときに起きる
MicrosoftのVBAリファレンスでは、エラー9は配列の範囲外要素や、存在しないコレクションメンバーなどを参照した場合に発生すると説明されています。Excel VBAでは、WorksheetsやWorkbooksもコレクションなので、名前や番号が実際の内容と一致しているかを最初に確認すると原因を絞り込みやすくなります。
| エラーが出たコード | 疑うポイント | 最初の確認 |
|---|---|---|
| Worksheets("Data") | その名前のシートが対象ブックに存在しない | 実際のシート名と参照しているブックを確認 |
| Workbooks("Report.xlsx") | その名前のブックが開いていない、または名前が違う | Workbooksコレクション内の名前を確認 |
| arr(i) | iが配列の下限~上限を外れている | LBound(arr)とUBound(arr)を確認 |
| values(0) など動的配列へのアクセス | ReDim前で要素領域が割り当てられていない | アクセス前にReDimされているか確認 |
| Worksheets(n) | nが1~Worksheets.Countの範囲外 | Countとnを比較 |
用語解説:コレクション
複数のWorkbookやWorksheetなどをまとめて扱うオブジェクトです。名前やインデックス番号で1要素を取り出せますが、存在しない要素を指定するとエラー9につながります。
シート名を指定した行で止まる場合
`Worksheets("売上")`のような参照で止まる場合は、そのシートが「どのブックに存在するはずか」を確認します。シート名の全角・半角、末尾スペース、名称変更だけでなく、参照しているブック自体が想定と違う場合もあります。
Sub CheckSheetNames()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Debug.Print ws.Name
Next ws
End Sub
イミディエイトウィンドウに実際のシート名を出せば、コード側の文字列と比較できます。マクロが入っているブックのシートを操作したいなら、`Worksheets("売上")`と省略せず`ThisWorkbook.Worksheets("売上")`のように対象ブックを明示すると、別ブックがアクティブになったときの取り違えを防ぎやすくなります。
Private Function WorksheetExists(ByVal wb As Workbook, ByVal sheetName As String) As Boolean
Dim ws As Worksheet
For Each ws In wb.Worksheets
If StrComp(ws.Name, sheetName, vbTextCompare) = 0 Then
WorksheetExists = True
Exit Function
End If
Next ws
End Function
Sub UseSheetSafely()
If Not WorksheetExists(ThisWorkbook, "売上") Then
MsgBox "シート『売上』が見つかりません。"
Exit Sub
End If
ThisWorkbook.Worksheets("売上").Range("A1").Value = "OK"
End Sub
ブック名を指定した行で止まる場合
`Workbooks("Report.xlsx")`でエラー9になる場合は、指定したブックが同じExcelインスタンスで開かれているか、`Workbook.Name`と文字列が一致しているかを確認します。保存形式が変わって`.xlsm`になった、別名保存した、といった変更でも参照文字列とのずれが起きます。
Sub CheckWorkbookNames()
Dim wb As Workbook
For Each wb In Application.Workbooks
Debug.Print wb.Name
Next wb
End Sub
マクロ自身が入っているブックを使う処理なら、名前で探すより`ThisWorkbook`を使えるか検討してください。`ThisWorkbook`はコードが実行されているブックを表し、現在前面にある`ActiveWorkbook`とは別物です。どちらを対象にしたいのかをコード上で明確にすることが重要です。
Private Function WorkbookIsOpen(ByVal bookName As String) As Boolean
Dim wb As Workbook
For Each wb In Application.Workbooks
If StrComp(wb.Name, bookName, vbTextCompare) = 0 Then
WorkbookIsOpen = True
Exit Function
End If
Next wb
End Function
配列の行で止まる場合はLBoundとUBoundを確認する
配列の添字は、宣言された範囲内でなければなりません。たとえば`Dim values(0 To 2) As String`なら使える添字は0、1、2で、`values(3)`は範囲外です。固定値でループ範囲を決めるより、LBoundとUBoundから実際の下限・上限を取得すると修正に強くなります。
Sub SafeArrayLoop()
Dim values(0 To 2) As String
Dim i As Long
values(0) = "A"
values(1) = "B"
values(2) = "C"
For i = LBound(values) To UBound(values)
Debug.Print values(i)
Next i
End Sub
配列の下限は宣言方法やOption Baseの影響を受けることがあるため、「必ず0から始まる」「必ず1から始まる」と決め打ちしない方が安全です。多次元配列では`LBound(values, 2)`、`UBound(values, 2)`のように次元を指定します。
(VBA配列の宣言・初期化や実務での扱いを整理したい場合については『VBA配列の使い方完全ガイド|高速化の仕組みとエラー対策まで』をご参照ください)
動的配列はReDim前にアクセスしない
`Dim values() As String`のように要素数を決めずに宣言した動的配列は、ReDimで範囲を割り当ててから使います。宣言しただけの状態で`values(0)`へ代入すると、参照できる要素がまだ存在しないためエラー9になります。
Sub DynamicArrayExample()
Dim values() As String
ReDim values(0 To 2)
values(0) = "A"
values(1) = "B"
values(2) = "C"
End Sub
途中で要素数を変更する場合も、どのタイミングでReDimされたかを追ってください。サイズ変更後に古い上限を使ってループすると、再び範囲外アクセスになる可能性があります。
番号指定のコレクション参照もCountを超えるとエラー9になる
`Worksheets(3)`や`Workbooks(2)`のような番号指定も、そのコレクションに存在する範囲内でなければなりません。処理中にシートを削除したり、開いているブック数が変わったりすると、固定インデックスが成立しなくなることがあります。
Sub CheckWorksheetIndex()
Dim index As Long
index = 3
If index < 1 Or index > ThisWorkbook.Worksheets.Count Then
MsgBox "指定したシート番号は範囲外です。"
Exit Sub
End If
Debug.Print ThisWorkbook.Worksheets(index).Name
End Sub
位置そのものに意味がない処理なら、`For Each`でコレクションを順に処理する方が、存在しない番号を指定するリスクを減らせます。名前で参照する場合も、処理前の存在確認を入れておくと原因を追いやすくなります。
エラー9を切り分けるときの確認順
- VBEでエラーが出た行を確認し、参照対象がシート・ブック・配列・その他コレクションのどれか特定する
- 文字列で参照している場合は、実際のNameをDebug.Printで出して比較する
- WorksheetsやWorkbooksを省略記法で書いている場合は、ThisWorkbookなど対象ブックを明示する
- 番号で参照している場合は、Countと比較して範囲内か確認する
- 配列ならLBound・UBoundで実際の範囲を確認する
- 動的配列ならReDimがアクセスより前に実行されているか確認する
- 修正後は存在確認や境界チェックを残し、同じ入力条件で再発しないか確認する
エラー9は「何かが存在しない」という一点は共通していますが、対象によって直し方が異なります。エラー処理で握りつぶす前に、どのコレクションまたは配列のどの要素を参照しようとしたかを確認するのが近道です。
(エラー9ではなく『型が一致しません』と表示されている場合については『VBA「実行時エラー13 型が一致しません」5つの原因と防御的プログラミングとは?』をご参照ください)
よくある質問
シートは見えているのにWorksheets("シート名")でエラー9になるのはなぜですか?
参照しているブックが想定と違う可能性があります。省略したWorksheets参照ではなく、ThisWorkbookや対象Workbook変数を明示し、そのブックのWorksheetsコレクションに目的のシートがあるか確認してください。
On Error Resume Nextでエラー9を回避してもよいですか?
存在確認のために限定的に使う実装はありますが、広い範囲をOn Error Resume Nextで囲むと別の不具合まで見逃しやすくなります。可能ならCount、LBound・UBound、For Eachなどで条件を明示してから参照する方が原因を追いやすくなります。
配列のループは0から始めればエラー9を防げますか?
必ずしも防げません。配列の下限は宣言方法によって異なるため、LBoundからUBoundまでを使ってループする方が安全です。
まとめ:止まった行の「参照先」を確認すれば原因を絞り込める
- シート名・ブック名は実際のNameと一致しているか確認する
- ThisWorkbookとActiveWorkbookのどちらを対象にするか明示する
- 配列はLBound・UBoundの範囲内でアクセスする
- 動的配列はReDim後にアクセスする
- コレクションの番号指定はCountの範囲内か確認する
まずエラーが発生した1行を見て、シート・ブック・配列のどれを参照しているかを特定してください。参照対象が分かれば、名前の存在確認か、添字の境界確認か、ReDimの実行順かに調査を絞れます。