Updated on

IndexOutOfRangeException is the exception .NET throws when we read or write an array element at an index that doesn’t exist. For a normal zero-based array, that is a negative index or one equal to or greater than the array’s Length. On arrays its message is “Index was outside the bounds of the array.”

The fix almost always sits in our own index arithmetic, and a try-catch around the line won’t repair it.

To download the source code for this article, you can visit our GitHub repository.

We’re going to learn about various situations where this exception gets raised and about the preventive measures we can take to make our applications more robust.

What Does “Index Was Outside the Bounds of the Array” Mean?

“Index was outside the bounds of the array” is the message of System.IndexOutOfRangeException. The runtime throws it when our code reads or writes an element at a position the array doesn’t have.

An array with five elements has valid indexes from 0 to 4. Index 5 is out of range, and so is any negative index. Strings and Span<T> throw the same exception with the same message, which is why an error on a string still mentions an array.

The full error comes with a stack trace, a list of at lines under the message. The first at line naming one of our own methods, such as a Button1_Click handler, is where the bad index was used.

VB.NET throws the same .NET type with the same message, so the fixes below work there too.

Microsoft Learn’s IndexOutOfRangeException reference calls it a developer error and says that, instead of handling the exception, we should “diagnose the cause of the error and correct your code.”

Another exception you’re familiar with is NullReferenceException. If you want to learn more, we have an article about NullReferenceException as well.

Array – Upper Bound

The upper bound of a zero-based array is one less than the array length.

Let’s create an integer array and run a for loop on it to demonstrate the exception:

var numbersArray = new int[] { 1, 2, 3, 4, 5 };

for (var i = 0; i <= numbersArray.Length; i++)
{
    Console.WriteLine(numbersArray[i]);
}

The for loop runs perfectly fine when i < 5, but it fails for i == 5 because the valid indexes are 0 to 4, so there is no element at index 5:

1
2
3
4
5
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.

How to Fix IndexOutOfRangeException?

Now, let’s see how we can fix IndexOutOfRangeException in this case.

Let’s remove the equality condition and just check for i < numbersArray.Length :

var numbersArray = new int[] { 1, 2, 3, 4, 5 };

for (var i = 0; i < numbersArray.Length; i++)
{
    Console.WriteLine(numbersArray[i]);
}

And if we run the application again, there’s no exception this time:

1
2
3
4
5

Array – Custom Lower Bound

We can create an array with a custom lower bound by using the Array.CreateInstance(Type elementType, int[] lengths, int[] lowerBounds) method:

var customLowerBoundArray = Array.CreateInstance(typeof(int), new int[] { 5 }, new int[] { 1 });
var value = 2;

for (var i = 0; i < customLowerBoundArray.Length; i++)
{
    customLowerBoundArray.SetValue(value, i);
    value *= 5;
}

The code snippet throws IndexOutOfRangeException when we try to set the array element’s value at an index lower than the custom lower bound.

The lower bound is 1, but the first element is set at position 0 (i == 0).

Solution

Let’s see how we can prevent the exception in this case:

var customLowerBoundArray = Array.CreateInstance(typeof(int), new int[] { 5 }, new int[] { 1 });
var value = 2;
 
for (var i = customLowerBoundArray.GetLowerBound(0); i <= customLowerBoundArray.GetUpperBound(0); i++)
{
    customLowerBoundArray.SetValue(value, i);
    value *= 5;
}

for (var i = customLowerBoundArray.GetLowerBound(0); i <= customLowerBoundArray.GetUpperBound(0); i++)
{
    Console.WriteLine(customLowerBoundArray.GetValue(i));
}

We make use of Array.GetLowerBound() and Array.GetUpperBound() methods to determine the start and end index of the custom lower bound array:

2
10
50
250
1250

List – Incorrect Arguments

This scenario is a little bit different:

Console.WriteLine("Please enter a search argument: ");

var searchArg = Convert.ToInt32(Console.ReadLine());
var multiplesOfFive = new List<int> { 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 };

Console.WriteLine($"Let's display the multiples of 5 starting at {searchArg}");

var startIndex = multiplesOfFive.IndexOf(searchArg);

for (var i = startIndex; i < multiplesOfFive.Count; i++)
{
    Console.WriteLine(multiplesOfFive[i]);
}

We have a List<int> to which we add the first 10 multiples of 5.

We take input from the user, searchArg, and display the numbers from that value on.

A number other than the ones present in the list results in an index of -1, which raises the ArgumentOutOfRangeException.

An input of 12 breaks the code and we get the exception:

Please enter a search argument:
12
Let's display the multiples of 5 starting at 12
Unhandled exception.
System.ArgumentOutOfRangeException:
Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')

Solution

To prevent the exception, we can add a check:

Console.WriteLine("Please enter a search argument: ");

var searchArg = Convert.ToInt32(Console.ReadLine());
var multiplesOfFive = new List<int> { 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 };

var startIndex = multiplesOfFive.IndexOf(searchArg);
if (startIndex < 0)
{
    Console.WriteLine($"The number {searchArg} does not exist in the list.");
}
else
{
    Console.WriteLine($"Let's display the multiples of 5 starting at {searchArg}");
    for (var i = startIndex; i < multiplesOfFive.Count; i++)
    {
        Console.WriteLine(multiplesOfFive[i]);
    }
}

An incorrect number (other than the numbers in the list) results in a user-friendly message:

Please enter a search argument:
22
The number 22 does not exist in the list.

IndexOutOfRangeException vs ArgumentOutOfRangeException

The exception type depends on the indexer we go through. Arrays, strings and spans throw IndexOutOfRangeException. List<T> throws ArgumentOutOfRangeException, and so does an array when we reach it through the IList<T> interface:

IList<string> tempList = new string[] { "0" };
var foo = tempList[-1]; //ArgumentOutOfRangeException

string[] tempArray = new string[] { "0" };
var bar = tempArray[-1]; //IndexOutOfRangeException

We ran the common indexers on .NET 10 and recorded the exception and the message each one throws.

Access (out of range)ExceptionMessage on .NET 10
array[5], array[-1], array[^0]IndexOutOfRangeExceptionIndex was outside the bounds of the array.
"hello"[5] (string indexer)IndexOutOfRangeExceptionIndex was outside the bounds of the array.
Span<int> indexerIndexOutOfRangeExceptionIndex was outside the bounds of the array.
int[2, 2] read at [2, 0]IndexOutOfRangeExceptionIndex was outside the bounds of the array.
List<int> indexer, [5] or [-1]ArgumentOutOfRangeExceptionIndex was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
Array read through IList<int>ArgumentOutOfRangeExceptionSame as List<int>
ArraySegment<int> indexerArgumentOutOfRangeExceptionSame as List<int>
array.ElementAt(9)ArgumentOutOfRangeExceptionSame as List<int>
array[3..7] on 5 elementsArgumentOutOfRangeExceptionSpecified argument was out of the range of valid values. (Parameter 'length')
"hello".Substring(10)ArgumentOutOfRangeExceptionstartIndex cannot be larger than length of string. (Parameter 'startIndex')
DataRow column row[3]IndexOutOfRangeExceptionCannot find column 3.
DataTable.Rows[10]IndexOutOfRangeExceptionThere is no row at position 10.
Dictionary<int, string> missing keyKeyNotFoundExceptionThe given key '2' was not present in the dictionary.
array.ElementAtOrDefault(9)noneReturns default (0 for int)

Two Arrays – Different Lengths

The IndexOutOfRangeException is raised when we attempt to assign an array element to a different array that has fewer elements than the first array, at an index the second array doesn’t have:

var array1 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var array2 = new int[7];

array2[array1.Length - 1] = array1[array1.Length - 1];

The array1 length is 10, while the array2 length is 7.

We are trying to assign the last element of array1 to array2, but since array2 is smaller (length is 7), we get the IndexOutOfRangeException.

Solution

Now, let’s see how we can prevent the exception:

var array1 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var array2 = new int[array1.Length];

array2[array1.Length - 1] = array1[array1.Length - 1];
Console.WriteLine($"The last element of array2 is: {array2[array2.Length - 1]}");

We initialize the second array as similar to the first array’s length:

The last element of array2 is: 10

Getting Confused Between an Index and the Value of That Index

Let’s see this case with an example:

var oddNumbersArray = new int[] { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
foreach (var number in oddNumbersArray)
{
    Console.WriteLine($"The odd number is: {oddNumbersArray[number]}");
}

We have an array of odd numbers, we run a foreach loop on the array and try to access the element using the index and not the element itself.

We start encountering  IndexOutOfRangeException when the number == 11 acts as the index.

There is no element at the 11th index:

The odd number is: 3
The odd number is: 7
The odd number is: 11
The odd number is: 15
The odd number is: 19
Unhandled exception. 
System.IndexOutOfRangeException: Index was outside the bounds of the array.

Solution

We can prevent the exception easily by using the value itself, instead of trying to access the value by index in each iteration:

var oddNumbersArray = new int[] { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
foreach (var number in oddNumbersArray)
{
    Console.WriteLine($"The odd number is: {number}");
}

This effectively prevents the exception:

The odd number is: 1
The odd number is: 3
The odd number is: 5
The odd number is: 7
The odd number is: 9
The odd number is: 11
The odd number is: 13
The odd number is: 15
The odd number is: 17
The odd number is: 19

If you’re unsure which loop fits, we compare for and foreach loops in a separate article.

DataTable – Incorrect Column Index

We create a method to render a static DataTable in a separate StaticDataRepository class file:

using System.Data;

public class StaticDataRepository
{
    public static DataTable GetGroceries()
    {
        var dataTable = new DataTable
        {
            TableName = "Groceries"
        };
        dataTable.Columns.Add("Id", typeof(int));
        dataTable.Columns.Add("Name", typeof(string));
        dataTable.Columns.Add("Description", typeof(string));

        dataTable.Rows.Add(1, "Macaroni", "Tasty Pasta");
        dataTable.Rows.Add(2, "Ramen", "Tasty Noodles");
        dataTable.Rows.Add(3, "Figaro Oil", "Olive Oil");
        dataTable.Rows.Add(4, "XYZ Lentils", "Nutritious Pulses");

        return dataTable;
    }
}

We then run a for loop over the rows and print four columns from each:

var groceries = StaticDataRepository.GetGroceries();
for (var i = 0; i < groceries.Rows.Count; i++)
{
    var row = groceries.Rows[i];
    Console.WriteLine($"{row[0]}\t {row[1]}\t {row[2]}\t {row[3]}");
}

The code raises an IndexOutOfRangeException because we are trying to access the 4th column (row[3]), which is not present in the data table:

Unhandled exception. 
System.IndexOutOfRangeException: Cannot find column 3.

It is quite possible that we might not be aware of the number of columns in the incoming data table.

We may try to access a column that does not exist and encounter the exception.

Solution

Let’s see how we can prevent the exception in this scenario:

using System.Data;

var groceries = StaticDataRepository.GetGroceries();
 
foreach (DataRow grocery in groceries.Rows)
{
    foreach (var item in grocery.ItemArray)
    {
        Console.WriteLine($"{item}");
    }
 
    Console.WriteLine("********************");
}

We do not hardcode the indexes, rather we make use of the inbuilt properties such as ItemArray that is an array of objects:

1
Macaroni
Tasty Pasta
********************
2
Ramen
Tasty Noodles
********************
3
Figaro Oil
Olive Oil
********************
4
XYZ Lentils
Nutritious Pulses
********************

These were the most common situations where we can stumble upon the IndexOutOfRangeException.

How Do We Avoid IndexOutOfRangeException in Modern C#?

Most fixes let the language track the bounds for us.

When we don’t need the position, foreach hands us each element and never produces an index to get wrong. When we do need the position, the loop condition is i < array.Length. A <= there is the classic off-by-one.

For the last element, C# 8 added the hat operator: numbersArray[^1] reads the last element without any Length - 1 arithmetic. Watch out for ^0, though. It points one past the end and throws the same IndexOutOfRangeException.

Ranges such as numbersArray[^3..] copy a slice into a new array. A range that runs past the end throws ArgumentOutOfRangeException instead.

We check an index that comes from user input or a search method like IndexOf() before using it. ElementAtOrDefault() does the check for us and returns the default value instead of throwing.

Catching the exception rarely helps. By the time it fires, the index math is already wrong, and a catch block hides where.

Let’s see the hat operator, a range and ElementAtOrDefault() on the same five-element array:

var numbersArray = new int[] { 1, 2, 3, 4, 5 };

Console.WriteLine($"The last element is: {numbersArray[^1]}");
Console.WriteLine($"The last three elements are: {string.Join(", ", numbersArray[^3..])}");
Console.WriteLine($"The tenth element or the default: {numbersArray.ElementAtOrDefault(9)}");

^1 gives us the last element, ^3.. copies the last three, and asking for the tenth element returns 0 instead of throwing:

The last element is: 5
The last three elements are: 3, 4, 5
The tenth element or the default: 0

Don’t do this. ^0 looks like “the end” but it points one past the last element:

Console.WriteLine(numbersArray[^0]);

It fails just like numbersArray[5] would:

Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.

If the hat operator and ranges are new to you, our article on ranges and indices covers them in depth.

Conclusion

We have learned about IndexOutOfRangeException in detail, the various scenarios where the exception gets raised, and the possible solutions.

Arrays, strings and spans throw IndexOutOfRangeException, while List<T> and most collection APIs throw ArgumentOutOfRangeException. Either way, the fix is in how we compute the index, and exception handling is for the errors we can’t prevent.

Tested with .NET 10.0.10 and MSTest 4.5.2.