System.IndexOutOfRangeException

An IndexOutOfRangeException is thrown when you use an index that is outside the bounds of an array. Arrays are zero-based, so an array with three elements has the indexes 0, 1 and 2. The message is Index was outside the bounds of the array.

It only applies to arrays. Collections such as List<T> throw an ArgumentOutOfRangeException for the same mistake, with a message that names the parameter.

Minimum version: >= 1.1 >= Core 1.0

Statistics

22
elmah.io logo 25

Common causes

The cause is always an index that is negative or not smaller than the length. These are the usual ways to end up there.

An off-by-one error in a loop

The last valid index is Length - 1. A loop condition that uses <= instead of < goes one step too far.

var names = new[] { "Ada", "Grace", "Linus" };

for (var i = 0; i <= names.Length; i++)
{
    Console.WriteLine(names[i]);   // IndexOutOfRangeException when i is 3
}

Use <, or better, a foreach loop that cannot go out of bounds:

var names = new[] { "Ada", "Grace", "Linus" };

foreach (var name in names)
{
    Console.WriteLine(name);
}

An array can be empty, for example when no command line arguments are passed or a query returns nothing. Index 0 does not exist then.

var items = Array.Empty<string>();
var first = items[0];   // IndexOutOfRangeException

Check the length first, or use a method that handles the missing element:

var items = Array.Empty<string>();
var first = items.Length > 0 ? items[0] : null;
var firstOrDefault = items.FirstOrDefault();

Data from files and users often has fewer parts than the code assumes. Splitting a line gives you only as many elements as there are separators.

var line = "Ada,1815";
var parts = line.Split(',');
var country = parts[2];   // IndexOutOfRangeException, there are only two parts

Check the number of parts, and report bad lines instead of crashing on them:

var line = "Ada,1815";
var parts = line.Split(',');

if (parts.Length < 3)
{
    Console.WriteLine($"Skipping line with {parts.Length} fields.");
    return;
}

var country = parts[2];

An index that is calculated, for example index - 1 to get the previous element, becomes negative at the start of the array.

var values = new[] { 10, 20, 30 };
var index = 0;
var previous = values[index - 1];   // IndexOutOfRangeException, the index is -1

Check the lower bound too, or use an index from the end:

var values = new[] { 10, 20, 30 };
var index = 0;
var previous = index > 0 ? values[index - 1] : 0;

var last = values[^1];

How to fix it and prevent it

Use foreach when you do not need the index

A foreach loop visits every element and cannot go out of bounds. Use a for loop only when the index itself matters.

A condition that checks index >= 0 && index < array.Length is cheap and removes the exception. ElementAtOrDefault does the same and returns the default value when the element is missing.

var parts = "Ada,1815".Split(',');

if (parts.Length > 2)
{
    Console.WriteLine(parts[2]);
}

var country = parts.ElementAtOrDefault(2) ?? "unknown";

In C# 8 and later, array[^1] is the last element and array[^2] the one before it. It removes the Length - 1 calculation, which is where many off-by-one errors start.

var values = new[] { 10, 20, 30 };
var last = values[^1];
var secondToLast = values[^2];

How to find the bad index

The stack trace points to the line that indexed the array:

System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Shop.Services.Report.Last() in C:\src\Shop\Services\Report.cs:line 12
   at Program.<Main>$(String[] args) in C:\src\Shop\Program.cs:line 4

Line 12 of Report.cs reads _names[_names.Length], which is one past the last element. The exception does not tell you which index or how long the array was, so add both to a log message or inspect them in the debugger. If a line indexes several arrays, split it up to see which one failed.

Should you catch it?

No. An IndexOutOfRangeException is a bug, and catching it hides the bug. Check the index, or validate the data, instead.

Catch it only at the edge of your application, where you log unexpected exceptions and return an error response.

Find IndexOutOfRangeException before your users do

elmah.io logs every unhandled exception in your .NET application with its stack trace and request details, groups identical errors and notifies you when a new one appears.

Start free trial
Free 21-day trial No credit card required

Related exceptions

Frequently asked questions

Why does List<T> throw ArgumentOutOfRangeException and not IndexOutOfRangeException?

Arrays are checked by the runtime and throw IndexOutOfRangeException. List<T> checks its own indexer and throws ArgumentOutOfRangeException with the parameter name index. The cause is the same.

Yes. An array with N elements has the indexes 0 to N - 1. Arrays with other lower bounds are possible, but rare.

Use array[^1] in C# 8 and later, or array[array.Length - 1] after checking that the array is not empty.

No. The exception only has the message. Log the index and the length where you index the array, or look at them in the debugger.

Let your AI agent track it down

Connect Claude Code, Cursor, VS Code or Visual Studio to the elmah.io MCP server and ask your agent to look into IndexOutOfRangeException for you. For example:

AI chat
Find the most frequent IndexOutOfRangeException in my production log and show me the line that throws it.

The agent reads the stack trace and request details from elmah.io, finds the code in your repository and proposes a fix. In Claude Code, add the server with one command:

claude mcp add --transport http --client-id claudecode elmahio https://mcp.elmah.io/mcp

The MCP server is included on every plan and is currently in beta. Set up the MCP server.

Further reading

YouTube videos

Answers from Stack Overflow

if (myReader["Additional"] != DBNull.Value)
{
    ltlAdditional.Text = "contains data";
}
else
{
     ltlAdditional.Text = "is null";
}

By Robert Durgin. Read the original answer on Stack Overflow.

There are two basic ways how to map bidirectional one-to-one association in NH. Let's say the classes look like this:

public class Setting
{
    public virtual Guid Id { get; set; }
    public virtual Student Student { get; set; }
}

public class Student
{
    public virtual Guid Id { get; set; }
    public virtual Setting Setting { get; set; }
}

Setting class is a master in the association ("aggregate root"). It is quite unusual but it depends on problem domain...

Primary key association

public SettingMap()
{
    Id(x => x.Id).GeneratedBy.Guid();
    HasOne(x => x.Student).Cascade.All();
}

public StudentMap()
{
    Id(x => x.Id).GeneratedBy.Foreign("Setting");
    HasOne(x => x.Setting).Constrained();
}

and a new setting instance should be stored:

        var setting = new Setting();

        setting.Student = new Student();
        setting.Student.Name = "student1";
        setting.Student.Setting = setting;
        setting.Name = "setting1";

        session.Save(setting);

Foreign key association

public SettingMap()
{
    Id(x => x.Id).GeneratedBy.Guid();
    References(x => x.Student).Unique().Cascade.All();
}

public StudentMap()
{
    Id(x => x.Id).GeneratedBy.Guid();
    HasOne(x => x.Setting).Cascade.All().PropertyRef("Student");
}

Primary key association is close to your solution. Primary key association should be used only when you are absolutely sure that the association will be always one-to-one. Note that AllDeleteOrphan cascade is not supported for one-to-one in NH.

EDIT: For more details see:

http://fabiomaulo.blogspot.com/2010/03/conform-mapping-one-to-one.html

http://ayende.com/blog/3960/nhibernate-mapping-one-to-one

By Jakub Linhart. Read the original answer on Stack Overflow.

You're accessing the list by different threads, but a list is not threadsafe:

You could lock the list with:

lock(listTotalCost)
    listTotalCost.Add(temp.Value);

Or use Concurrent collections.

By Jeroen van Langen. Read the original answer on Stack Overflow.

With Dictionary<,> you have to lock both reading and writing. So both

lock( ConnectionList ) {
   ConnectionList.Add( key, res );
}

and

lock( ConnectionList ) {
   res = ConnectionList[ key ];
}

and

lock( ConnectionList ) {
   int cnt = ConnectionList.Count;
}

and

lock( ConnectionList ) {
   ConnectionList.Clear();
}

and

lock( ConnectionList ) {
   foreach ( var kv in ConnectionList ) {
      // Do things
   }
}

and so on :-)

With ConcurrentDictionary<,> you don't need any locking, but note that the syntax is a little different than the one of the Dictionary<,>

By xanatos. Read the original answer on Stack Overflow.

I haven't used DataReaders for 3+ years, so I wanted to confirm my memory and found this. Anyway, for anyone who happens upon this post like I did and wants a method to test IsDBNull using the column name instead of ordinal number, and you are using VS 2008+ (& .NET 3.5 I think), you can write an extension method so that you can pass the column name in:

public static class DataReaderExtensions
{
    public static bool IsDBNull( this IDataReader dataReader, string columnName )
    {
        return dataReader[columnName] == DBNull.Value;
    }
}

Kevin

By Kevin Nelson. Read the original answer on Stack Overflow.