「昨日まで動いていたマクロが、今朝いきなり止まった」
画面に出ているのは、実行時エラー ‘9’: インデックスが有効範囲にありません。ボタンは[デバッグ]と[終了]しかない。もう心が折れそうですよね。
大丈夫です。このエラーは、VBAが「あなたが指定した名前・番号のモノが見つかりません」と言っているだけです。原因はほぼ7つに絞れます。しかも、その中で圧倒的に多いのは「シート名のちょっとした違い」。1文字の全角スペースや、末尾の空白が犯人であることが本当に多いのです。
この記事では、[デバッグ]を押して黄色い行を見るところから始めて、原因を1つずつ潰していきます。コードはすべてコピペで試せる形にしました。VBAを書き始めたばかりの方でも、上から順に読めば必ずどこかで止まった理由に行き当たります。

- まず[デバッグ]を押して「黄色い行」を見る(症状別 早見表)
- そもそも「実行時エラー9」とは何が起きているのか
- 原因1:シート名の指定ミス・存在しないシートを参照している(最多)
- 原因2:ブック名の指定ミス・そのブックが開いていない
- 原因3:配列の要素数を指定していない・範囲外を指定している
- 原因4:ループの上限が要素数を超えている
- 原因5:Splitで作った配列の添字がずれている
- 原因6:「0番目」を指定している(VBAのコレクションは1始まり)
- 原因7:CollectionやDictionaryに無いキーを取り出している
- 二度と止まらないための書き方(ここが本当の解決です)
- よくある質問(Q&A)
- まとめ:エラー9は「名前が違う」と教えてくれているだけ
- 次に読むと、もっと自動化が進みます
- 出典(一次情報)
まず[デバッグ]を押して「黄色い行」を見る(症状別 早見表)
エラーが出たら、迷わず[デバッグ]を押してください。止まった行が黄色く反転します。そこがすべての出発点です。黄色い行に何が書いてあるかで、原因はほぼ絞り込めます。
| 黄色い行に書いてあるもの | いちばん疑うべき原因 | この記事の該当箇所 |
|---|---|---|
Worksheets("集計") / Sheets("Sheet1") |
シート名の指定ミス・存在しないシート | 原因1 |
Workbooks("data.xlsx") |
そのブックが開いていない・拡張子違い | 原因2 |
arr(5) のような配列 |
要素数を指定していない/範囲外を指定 | 原因3 |
For i = 1 To ... の中の行 |
ループの上限が要素数を超えている | 原因4 |
Split(...) の結果を使う行 |
区切り文字が無く、配列が1個しかない | 原因5 |
Worksheets(0) / Cells(0, 1) |
「0番目」を指定している(1始まりです) | 原因6 |
coll("キー名") / dic("キー名") |
存在しないキーを取り出している | 原因7 |
ここから先は、上の表の順に1つずつ見ていきます。あなたの黄色い行に近いところから読んでも構いません。
そもそも「実行時エラー9」とは何が起きているのか
Microsoftの公式ドキュメントでは、このエラーは「下付き文字が有効範囲にありません (エラー 9)」という名前で説明されています。Excel VBAの画面上では「インデックスが有効範囲にありません」と表示されますが、中身は同じものです。
「下付き文字(Subscript)」とは、arr(3) の「3」や、Worksheets("集計") の「集計」のこと。つまりカッコの中で指定している番号や名前です。
公式ドキュメントには、こう書かれています。配列の要素やコレクションのメンバーには、定義されている範囲内でしかアクセスできない——と。
言い換えると、エラー9は次の一言に集約されます。
「その名前/その番号のモノは、存在しません」
コードの文法が間違っているのではありません。文法は正しいのに、指し示した先に中身が無かっただけ。だからこそ、直し方も「存在するものを正しく指す」の一点に尽きます。難しい理屈は要りません。
なお、似た番号のエラーに「実行時エラー91(オブジェクト変数またはWithブロック変数が設定されていません)」があります。こちらは「変数に中身を入れ忘れた(Setの書き忘れ)」が主因で、エラー9とは原因が異なります。見分け方は後半のQ&Aで整理します。
原因1:シート名の指定ミス・存在しないシートを参照している(最多)
エラー9の相談で、体感的にいちばん多いのがこれです。Worksheets("集計") と書いたのに、実際のシート名が「集計 」(末尾に半角スペース)だった、というような話です。
まず「本当のシート名」を出力して確かめる
思い込みを捨てて、実際のシート名を機械に言わせましょう。以下をコピペして実行してください。
Sub シート名を全部出す()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Debug.Print "[" & ws.Name & "]"
Next ws
End Sub
実行したら、VBEの画面で Ctrl + G を押して「イミディエイトウィンドウ」を開きます。そこに [集計] のように角カッコ付きで表示されます。角カッコの内側に余計なスペースが無いかを目で確認してください。[集計 ] になっていたら、それが犯人です。
つまずきやすい5つのパターン
- 末尾・先頭の半角/全角スペース……見た目では絶対に分かりません。上の出力で確認を。
- 全角と半角の違い……
Sheet1とSheet1は別物です。 - 大文字・小文字……VBAのシート名指定は基本的に大小を区別しませんが、スペルミスは当然アウトです。
- シート名を後から変更した……「集計」→「集計表」に変えた瞬間、マクロは止まります。
- シートを削除した/別のブックに移動した……当然、参照先がなくなります。
コピペで使える「安全な書き方」
そもそもシート名を直接書かずに、存在チェックしてから使うのが確実です。次の関数をモジュールに貼っておくと、以後ずっと使えます。
' シートが存在するかを判定する関数
Function シートがある(ByVal シート名 As String, _
Optional ByVal 対象ブック As Workbook) As Boolean
Dim ws As Worksheet
If 対象ブック Is Nothing Then Set 対象ブック = ThisWorkbook
For Each ws In 対象ブック.Worksheets
If ws.Name = シート名 Then
シートがある = True
Exit Function
End If
Next ws
シートがある = False
End Function
' 使い方
Sub 集計を実行()
If Not シートがある("集計") Then
MsgBox "「集計」という名前のシートが見つかりません。" & vbCrLf & _
"シート名を確認してください。", vbExclamation
Exit Sub
End If
With ThisWorkbook.Worksheets("集計")
.Range("A1").Value = "処理開始"
End With
End Sub
これで、エラーで強制停止する代わりに「シートが見つかりません」という親切なメッセージが出るようになります。翌朝の自分や、同僚が使うときに効きます。
非表示シートは「存在している」ので大丈夫
よくある誤解ですが、非表示(Hidden)のシートはエラー9の原因になりません。非表示でもシートは存在しているので、Worksheets("集計") で普通に参照できます(値の読み書きも可能です)。ただし Select や Activate は非表示シートに対してできないため、そちらは別のエラーになります。
「シート見出しごと消えていて、そもそもどこにあるか分からない」という場合は、こちらもあわせてどうぞ。

原因2:ブック名の指定ミス・そのブックが開いていない
次に多いのが Workbooks("データ.xlsx") の行で止まるケースです。VBAの Workbooks コレクションには、今このExcelで開いているブックしか入っていません。フォルダに置いてあるだけのファイルは対象外です。
つまずきポイントは主に3つ
- 拡張子を書いていない……
Workbooks("データ")は多くの環境で失敗します。"データ.xlsx"と拡張子まで書くのが安全です。 - そもそも開いていない……閉じているファイルは
Workbooks(...)では掴めません。Workbooks.Openで開いてから使います。 - フルパスを渡している……
Workbooks("C:\業務\データ.xlsx")はエラーになります。Workbooksに渡すのはファイル名だけです。
開いていなければ自動で開く書き方
Function ブックを確保(ByVal フルパス As String) As Workbook
Dim ファイル名 As String
Dim wb As Workbook
ファイル名 = Dir(フルパス) ' パスからファイル名だけ取り出す
If ファイル名 = "" Then
MsgBox "ファイルが見つかりません:" & vbCrLf & フルパス, vbExclamation
Exit Function
End If
On Error Resume Next
Set wb = Workbooks(ファイル名) ' すでに開いていれば掴む
On Error GoTo 0
If wb Is Nothing Then
Set wb = Workbooks.Open(フルパス) ' 開いていなければ開く
End If
Set ブックを確保 = wb
End Function
' 使い方
Sub 別ブックから読み込む()
Dim wb As Workbook
Set wb = ブックを確保("C:\業務\データ.xlsx")
If wb Is Nothing Then Exit Sub
MsgBox wb.Worksheets(1).Range("A1").Value
End Sub
「閉じているブックを直接さわろうとして止まる」パターンは、実行時エラー1004でも定番の原因です。あわせて読むと理解が早いです。


原因3:配列の要素数を指定していない・範囲外を指定している
ここからは配列の話です。公式ドキュメントが最初に挙げているのが、この原因です。
次のコードは必ずエラー9になります。
Sub これは止まる()
Dim MyArray() As Integer
MyArray(8) = 234 ' ← ここでエラー9
End Sub
理由はシンプルで、Dim MyArray() As Integer は「配列にします」と宣言しただけで、箱を1つも作っていないからです。公式ドキュメントも「Visual Basicでは、配列の範囲が指定されていない場合に0〜10のように暗黙的に次元が設定されることはない」と明記しています。箱の数は、自分で指定しなければならないのです。
直し方:DimかReDimで要素数を決める
Sub 直したもの()
' 方法A:最初から個数が決まっているなら Dim で指定
Dim 配列A(1 To 10) As Integer
配列A(8) = 234
' 方法B:あとから個数が決まるなら ReDim で確保
Dim 配列B() As Integer
ReDim 配列B(1 To 10)
配列B(8) = 234
' 方法C:中身を残したまま広げたいときは Preserve
ReDim Preserve 配列B(1 To 20)
配列B(15) = 999
MsgBox 配列A(8) & " / " & 配列B(8) & " / " & 配列B(15)
End Sub
ReDim Preserve を付け忘れると中身が全部消えるので、そこだけ注意してください。
「今いくつあるか」を必ず確認してから触る
範囲外を触らないためのいちばん確実な方法は、上限と下限を自分で数えないことです。LBound(下限)と UBound(上限)を使いましょう。
Sub 安全に全部まわす()
Dim 名前リスト As Variant
Dim i As Long
名前リスト = Array("田中", "佐藤", "鈴木")
For i = LBound(名前リスト) To UBound(名前リスト)
Debug.Print i & "番目:" & 名前リスト(i)
Next i
End Sub
この書き方なら、要素が3個でも300個でも、絶対に範囲外になりません。エラー9対策として、これ以上に効く習慣はありません。
原因4:ループの上限が要素数を超えている
原因3と地続きですが、実務で圧倒的にやりがちなのがこちらです。
Sub 危ない書き方()
Dim 名前リスト As Variant
Dim i As Long
名前リスト = Array("田中", "佐藤", "鈴木") ' 0〜2 の3個
For i = 1 To 3 ' ← 1から始めてしまった
Debug.Print 名前リスト(i) ' i=3 でエラー9
Next i
End Sub
Array 関数で作った配列は、既定では0番から始まります。つまり3個の要素は 0・1・2 であって、1・2・3 ではありません。i = 3 になった瞬間に「そんな箱はありません」で止まります。
直し方は2つ
' 直し方A(推奨):LBound〜UBoundで回す
For i = LBound(名前リスト) To UBound(名前リスト)
Debug.Print 名前リスト(i)
Next i
' 直し方B:For Each を使い、番号を意識しない
Dim 名前 As Variant
For Each 名前 In 名前リスト
Debug.Print 名前
Next 名前
公式ドキュメントも、コレクションを扱うときはインデックス要素を指定する代わりに For Each...Next を使う方法を挙げています。番号を書かなければ、番号でミスすることもありません。
なお、モジュールの先頭に Option Base 1 と書くと、そのモジュール内の配列の既定の下限を1に変えることができます。ただし「このモジュールだけ1始まり」という状態は後で混乱の元になりやすいので、慣れないうちは Option Base に頼らず LBound/UBound で書くほうが安全です。
原因5:Splitで作った配列の添字がずれている
CSVやテキストを分解するときの定番、Split 関数。ここでもエラー9はよく起きます。
Sub 危ないSplit()
Dim 行 As String
Dim 列() As String
行 = "田中太郎" ' カンマが1つも無い
列 = Split(行, ",")
MsgBox 列(1) ' ← エラー9(列(0)しか存在しない)
End Sub
Split は、区切り文字が見つからなければ「元の文字列がまるごと入った、要素1個の配列」を返します。つまり存在するのは 列(0) だけ。列(1) を触った瞬間に落ちます。
コピペで使える安全なSplit
Sub 安全なSplit()
Dim 行 As String
Dim 列() As String
Dim i As Long
行 = "田中太郎,営業部,2026/04/01"
列 = Split(行, ",")
' 期待する列数があるかを先に確認する
If UBound(列) < 2 Then
MsgBox "データの形式が想定と違います:" & vbCrLf & 行, vbExclamation
Exit Sub
End If
For i = LBound(列) To UBound(列)
Debug.Print i & " → " & Trim(列(i))
Next i
End Sub
ポイントは UBound(列) < 2 のチェックです。「触る前に、あるかどうかを聞く」。これだけでエラー9の大半は防げます。
原因6:「0番目」を指定している(VBAのコレクションは1始まり)
配列は0始まりのことが多いのに、Excelのコレクションは1始まり。この不一致が混乱を生みます。
| 書き方 | 結果 | 理由 |
|---|---|---|
Worksheets(0) |
❌ エラー9 | シートは1番から数える |
Worksheets(1) |
✅ 左端のシート | 正しい |
Cells(0, 1) |
❌ エラー1004など | 行番号は1から |
Array("a","b")(0) |
✅ “a” | Array は既定で0始まり |
Split("a,b", ",")(0) |
✅ “a” | Split は常に0始まり |
また、シートの枚数を超える番号も同じくエラー9です。
Sub 枚数を確認してから触る()
Dim n As Long
n = ThisWorkbook.Worksheets.Count
Debug.Print "シートは " & n & " 枚あります"
If n >= 3 Then
Debug.Print ThisWorkbook.Worksheets(3).Name
Else
MsgBox "3枚目のシートがありません(現在 " & n & " 枚)", vbExclamation
End If
End Sub
ちなみにシートの「番号」で指定するのは、そもそもおすすめしません。誰かがシートを並べ替えた瞬間に、まったく別のシートを処理してしまうからです。可能なかぎり名前で指定し、原因1の存在チェックとセットで使ってください。
原因7:CollectionやDictionaryに無いキーを取り出している
少し進んだ使い方をしている方向けです。Collection や Dictionary で、登録していないキーを取り出そうとするとエラー9になります。
Sub 危ないCollection()
Dim c As New Collection
c.Add "りんご", "apple"
MsgBox c("banana") ' ← エラー9(bananaは登録していない)
End Sub
Collectionの安全な取り出し方
Function コレクションから取得(ByVal c As Collection, _
ByVal キー As String) As String
On Error Resume Next
コレクションから取得 = c(キー)
If Err.Number <> 0 Then
コレクションから取得 = "" ' 見つからなければ空文字
Err.Clear
End If
On Error GoTo 0
End Function
Dictionaryなら Exists で確認できる(こちらが読みやすい)
Sub 安全なDictionary()
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
dic.Add "apple", "りんご"
dic.Add "orange", "みかん"
If dic.Exists("banana") Then
MsgBox dic("banana")
Else
MsgBox "banana は登録されていません", vbInformation
End If
End Sub
Dictionary には Exists という「あるかどうかを聞く」メソッドが用意されています。エラー処理でごまかすより、こちらのほうが読みやすく、後から自分が読み返したときにも意図が伝わります。
二度と止まらないための書き方(ここが本当の解決です)
原因を1つ直しても、また別の場所で同じことが起きたら意味がありません。エラー9が構造的に起きにくいコードにしてしまいましょう。習慣は3つだけです。
習慣1:モジュールの先頭に Option Explicit を書く
Option Explicit
変数の宣言を強制する1行です。これを書いておくと、集計シート と書くつもりで 集計シ-ト とタイプミスした場合に、実行前にVBEが教えてくれます。VBEのメニュー[ツール]→[オプション]→[編集]で「変数の宣言を強制する」にチェックを入れれば、以後の新規モジュールに自動で挿入されます。
習慣2:番号ではなく「名前」で指定する
' ❌ 並べ替えられたら壊れる
ThisWorkbook.Worksheets(2).Range("A1").Value = 100
' ✅ 名前なら並び順に影響されない
ThisWorkbook.Worksheets("集計").Range("A1").Value = 100
習慣3:触る前に「あるか」を聞く
この記事で何度も出てきた考え方です。まとめて1つのテンプレートにしておきます。そのままコピペして使えます。
Option Explicit
' ===== 汎用テンプレート:エラー9を出さない処理の型 =====
Sub 安全に処理する()
Dim ws As Worksheet
Dim データ As Variant
Dim i As Long
' (1) シートの存在を確認してから掴む
If Not シートがある("集計") Then
MsgBox "シート「集計」が見つかりません。名前を確認してください。", vbExclamation
Exit Sub
End If
Set ws = ThisWorkbook.Worksheets("集計")
' (2) 配列は必ず LBound〜UBound で回す
データ = Array("4月", "5月", "6月")
For i = LBound(データ) To UBound(データ)
ws.Cells(i - LBound(データ) + 1, 1).Value = データ(i)
Next i
MsgBox "処理が終わりました", vbInformation
End Sub
Function シートがある(ByVal シート名 As String, _
Optional ByVal 対象ブック As Workbook) As Boolean
Dim ws As Worksheet
If 対象ブック Is Nothing Then Set 対象ブック = ThisWorkbook
For Each ws In 対象ブック.Worksheets
If ws.Name = シート名 Then
シートがある = True
Exit Function
End If
Next ws
End Function
この型を1つ持っておくだけで、明日からのマクロは見違えるほど止まらなくなります。
よくある質問(Q&A)
Q1. エラー9とエラー91は、どう見分ければいいですか?
メッセージで区別できます。
- エラー9(インデックスが有効範囲にありません)……「その名前・その番号のモノが存在しない」。シート名の間違い、配列の範囲外など。
- エラー91(オブジェクト変数またはWithブロック変数が設定されていません)……「変数にまだ中身を入れていない」。
Setの書き忘れが最多。
91のほうで止まっている方は、こちらに原因7つと直し方をまとめています。

Q2. 昨日まで動いていたのに、今日だけ止まります。なぜ?
コードは変えていなくても、データ側が変わっている可能性が高いです。よくあるのは次の3つ。
- 誰かがシート名を変更した/シートを削除した
- 読み込むCSVの列数が変わった(原因5のパターン)
- いつも開いていた別ブックが、今日は閉じたままだった(原因2のパターン)
Q3. On Error Resume Next で無視してはいけませんか?
全体にかけるのはおすすめしません。 エラーを消しても原因は残るため、結果だけが静かに間違ったまま出てきます。使うなら、原因2や原因7で示したように「1行だけ・確認のために」に限定し、直後に On Error GoTo 0 で必ず戻してください。
Q4. そもそもマクロ自体が動かないのですが……
エラー9以前に、マクロの有効化やファイル形式(.xlsm)でつまずいているケースもあります。その場合はこちらから。

Q5. 配列の下限は、結局0と1のどちらで書くべきですか?
どちらでも構いません。大事なのは「自分で決め打ちしない」ことです。LBound と UBound で書いておけば、0始まりでも1始まりでも同じコードが動きます。迷ったらこの書き方を選んでください。

まとめ:エラー9は「名前が違う」と教えてくれているだけ
最後に要点を整理します。
- まず[デバッグ]で黄色い行を見る。原因の8割はここで分かります。
- エラー9の意味は「その名前・その番号のモノが存在しない」の一点だけ。
- 最多はシート名のズレ。
Debug.Print "[" & ws.Name & "]"で実物を確認する。 - 配列は必ず
LBound〜UBoundで回す。番号を自分で書かない。 - 触る前に「あるか」を聞く。存在チェック関数を1つ持っておく。
VBAのエラー番号は、正直こわいですよね。赤い文字と英語まじりのメッセージが並ぶと、「自分にはやっぱり向いていないのかも」と感じてしまうかもしれません。
でも、思い出してください。エラー9は「シートの名前、1文字違いますよ」と言っているだけなのです。あなたの能力を否定しているわけではありません。 むしろExcelは、間違ったまま黙って処理を進めるのではなく、わざわざ手を止めて教えてくれています。これほど親切な同僚は、なかなかいません。
今日、この記事のテンプレートを1つコピペしてモジュールに貼ってみてください。それだけで、明日のあなたは「エラーが出ても直せる人」になっています。小さな一歩ですが、確実に前に進んでいます。
次に読むと、もっと自動化が進みます
エラー番号ごとの対処をひととおり押さえておくと、マクロは驚くほど止まらなくなります。



出典(一次情報)
- Microsoft Learn「下付き文字が有効範囲にありません (エラー 9)」
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/subscript-out-of-range-error-9 - Microsoft Learn「ReDim ステートメント (VBA)」
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/redim-statement - Microsoft Learn「Option Base ステートメント (VBA)」
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/option-base-statement
