Troubleshooting Exceptions: System.InvalidOperationException
An InvalidOperationException is thrown when a method of an object is called when the state of the object cannot support the method call. The exception is also thrown when a method attempts to manipulate the UI from a thread that is not the main or UI thread.
Important
Because InvalidOperationExceptions can be thrown in a wide variety of circumstances, it's important to read and understand the Message that is displayed in the Exception Assistant.
In this article
A method running on a non-UI thread updates the UI
A statement in a foreach (For Each in Visual Basic) block changes the collection it is iterating
A Nullable<T> that is null is cast to T
A System.Linq.Enumerable method is called on an empty collection
Related articles
The code examples in this article show you how some common InvalidOperationException exceptions can occur in your app. How you handle the issues depends on the specific situation. If the exception is fatal to the functionality of your app, you might want to use a try … catch (Try .. Catch in Visual Basic) construct to capture the exception and clean up the app's state before you exit. But other InvalidOperationExceptions can be anticipated and avoided. The revised method examples show you some of these techniques.
A method running on a non-UI thread updates the UI
Causing an InvalidOperationException with a UI update from a non-UI thread | Avoiding InvalidOperationExceptions on non-UI threads
Most .NET GUI (graphical user interface) app frameworks, such as Windows Forms and Windows Presentation Foundation (WPF), let you access GUI objects only from the thread that creates and manages the UI (the Main or UI thread). An InvalidOperationException is thrown when you try to access a UI element from a thread that is not the UI thread.
Causing an InvalidOperationException with a UI update from a non-UI thread
Note
The following examples use the Task-based Asynchronous Pattern (TAP) to create non-UI threads. However, the examples are also relevant to all .NET Asynchronous Programming Patterns.
In these examples, the ThreadsExampleBtn_Click event handler calls the DoSomeWork method twice. The first call to the method (DoSomeWork(20); runs sychronously and succeeds. But the second call (Task.Run( () => { DoSomeWork(1000);});) runs on a thread in the app's thread pool. Because this call attempts to update the UI from a non-UI thread, the statement throws a InvalidOperationException
WPF and Store apps
Note
In Store Phone apps, an Exception is thrown instead of the more specific InvalidOperationException.
Exception messages:
WPF apps |
Additional information: The calling thread cannot access this object because a different thread owns it. |
Store apps |
Additional information: The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)) |
private async void ThreadsExampleBtn_Click(object sender, RoutedEventArgs e)
{
TextBox1.Text = String.Empty;
TextBox1.Text = "Simulating work on UI thread.\n";
DoSomeWork(20);
TextBox1.Text += "Simulating work on non-UI thread.\n";
await Task.Run(() => DoSomeWork(1000));
TextBox1.Text += "ThreadsExampleBtn_Click completes. ";
}
private void DoSomeWork(int msOfWork)
{
// simulate work
var endTime = DateTime.Now.AddMilliseconds(msOfWork);
while (DateTime.Now < endTime)
{
// spin
};
// report completion
var msg = String.Format("Some work completed in {0} ms on UI thread. \n", msOfWork);
TextBox1.Text += msg;
}
Windows Forms apps
Exception message:
- Additional information: Cross-thread operation not valid: Control 'TextBox1' accessed from a thread other than the thread it was created on.
private async void ThreadsExampleBtn_Click(object sender, EventArgs e)
{
TextBox1.Text = String.Empty;
var tbLinesList = new List<string>() {"Simulating work on UI thread."};
TextBox1.Lines = tbLinesList.ToArray();
DoSomeWork(20, tbLinesList);
tbLinesList.Add("Simulating work on non-UI thread.");
TextBox1.Lines = tbLinesList.ToArray();
await Task.Run(() => DoSomeWork(1000, tbLinesList));
tbLinesList.Add("ThreadsExampleBtn_Click completes.");
TextBox1.Lines = tbLinesList.ToArray();
}
private void DoSomeWork(int msOfWork, List<string> tbLinesList)
{
// simulate work
var endTime = DateTime.Now.AddMilliseconds(msOfWork);
while (DateTime.Now < endTime) { };
{
// spin
};
// report completion
var msg = String.Format("Some work completed in {0} ms on UI thread. \n", msOfWork);
tbLinesList.Add(msg);
TextBox1.Lines = tbLinesList.ToArray();
}
In this articleIn this section
Avoiding InvalidOperationExceptions on non-UI threads
Windows UI frameworks implement a dispatcher pattern that includes a method to check whether a call to a member of a UI element is being executed on the UI thread, and other methods to schedule the call on the UI thread.
WPF apps
In WPF apps, use one of the Dispatcher.Invoke methods to excecute a delegate on the UI thread. If necessary, use the Dispatcher.CheckAccess method to determine if a method is running on a non-UI thread.
private void DoSomeWork(int msOfWork)
{
var endTime = DateTime.Now.AddMilliseconds(msOfWork);
while (DateTime.Now < endTime)
{
// spin
};
// report completion
var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
var msg = String.Empty;
if (TextBox1.Dispatcher.CheckAccess())
{
msg = String.Format(msgFormat, msOfWork, String.Empty);
TextBox1.Text += msg;
}
else
{
msg = String.Format(msgFormat, msOfWork, "non-");
Action act = ()=> {TextBox1.Text += msg;};
TextBox1.Dispatcher.Invoke(act);
}
}
Windows Forms apps
In Windows Form apps, use the Control.Invoke method to excecute a delegate that updates the UI thread. If necessary, use the Control.InvokeRequired property to determine if a method is running on a non-UI thread.
private void DoSomeWork(int msOfWork, List<string> tbLinesList)
{
// simulate work
var endTime = DateTime.Now.AddMilliseconds(msOfWork);
while (DateTime.Now < endTime)
{
// spin
};
// report completion
var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
var msg = String.Empty;
if (TextBox1.InvokeRequired)
{
msg = String.Format(msgFormat, msOfWork, "non-");
tbLinesList.Add(msg);
Action act = () => TextBox1.Lines = tbLinesList.ToArray();
TextBox1.Invoke( act );
}
else
{
msg = String.Format(msgFormat, msOfWork, String.Empty);
tbLinesList.Add(msg);
TextBox1.Lines = tbLinesList.ToArray();
}
}
Store apps
In Store apps, use the CoreDispatcher.RunAsync method to excecute a delegate that updates the UI thread. If necessary, use the HasThreadAccess property to determine if a method is running on a non-UI thread.
private void DoSomeWork(int msOfWork)
{
// simulate work
var endTime = DateTime.Now.AddMilliseconds(msOfWork);
while (DateTime.Now < endTime)
{
// spin
};
// report completion
var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
var msg = String.Empty;
if (TextBox1.Dispatcher.HasThreadAccess)
{
msg = String.Format(msgFormat, msOfWork, String.Empty);
TextBox1.Text += msg;
}
else
{
msg = String.Format(msgFormat, msOfWork, "non-");
TextBox1.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
()=> {TextBox1.Text += msg;});
}
}
In this articleIn this section
A statement in a foreach (For Each in Visual Basic) block changes the collection it is iterating
Causing an InvalidOperationException with foreach | Avoiding InvalidOperationExceptions in loops
A foreach statement (For Each in Visual Basic) repeats a group of embedded statements for each element in an array or collection that implements the IEnumerable or IEnumerable interface. The foreach statement is used to iterate through the collection to read and modify the elements, but it can't be used to add or remove items from the collection. An InvalidOperationException is thrown if you add or remove items from the source collection in a foreach statement.
Causing an InvalidOperationException with foreach
In this example, an InvalidOperationException is thrown when the numList.Add(5); statement attempts to modify list in the foreach block.
Exception message:
- Additional information: Collection was modified; enumeration operation may not execute.
private void AddElementsToAList()
{
var numList = new List<int>() { 1, 2, 3, 4 };
foreach (var num in numList)
{
if (num == 2)
{
numList.Add(5);
}
}
// Display list elements
foreach (var num in numList)
{
Console.Write("{0} ", num);
}
}
In this articleIn this section
Avoiding InvalidOperationExceptions in loops
Important
Adding or removing elements to a list while you are interating over the collection can have unintended and hard to predict side effects. If possible, you should move the operation outside of the iteration.
private void AddElementsToAList ()
{
var numList = new List<int>() { 1, 2, 3, 4 };
var numberOf2s = 0;
foreach (var num in numList)
{
if (num == 2)
{
numberOf2s++;
}
}
for (var i = 0; i < numberOf2s; i++ )
{
numList.Add(5);
}
// Display list elements
foreach (var num in numList)
{
Console.Write("{0} ", num);
}
}
If your situation requires you to add or remove elements to a list as you iterate a collection, use a for (For in Visual Basic) loop:
private void AddElementsToAList ()
{
var numList = new List<int>() { 1, 2, 3, 4 };
for (var i = 0; i < numList.Count; i++)
{
if (numList[i] == 2)
{
numList.Add(5);
}
}
// Display list elements
foreach (var num in numList)
{
Console.Write("{0} ", num);
}
}
In this articleIn this section
A Nullable<T> that is null is cast to T
Causing an InvalidOperationException with an invalid cast | Avoiding InvalidOperationException from a bad cast
If you cast a Nullable structure that is null (Nothing in Visual Basic) to its underlying type, an InvalidOperationException exception is thrown.
Causing an InvalidOperationException with an invalid cast
In this method, an InvalidOperationException is thrown when the Select method casts a null element of dbQueryResults to an int.
Exception message:
- Additional information: Nullable object must have a value.
private void MapQueryResults()
{
var dbQueryResults = new int?[] { 1, 2, null, 4 };
var ormMap = dbQueryResults.Select(nullableInt => (int)nullableInt);
//display map list
foreach (var num in ormMap)
{
Console.Write("{0} ", num);
}
Console.WriteLine();
}
In this articleIn this section
Avoiding InvalidOperationException from a bad cast
To avoid InvalidOperationException:
Use the Nullable.HasValue property to select only those elements that are not null.
Use one of the overloaded Nullable.GetValueOrDefault methods to provide a default value for a null item.
Nullable<T>.HasValue example
private void MapQueryResults()
{
var dbQueryResults = new int?[] { 1, 2, null, 4 };
var ormMap = dbQueryResults
.Where(nulllableInt => nulllableInt.HasValue)
.Select(num => (int)num);
//display map list
foreach (var num in ormMap)
{
Console.Write("{0} ", num);
}
Console.WriteLine();
// handle nulls
Console.WriteLine("{0} nulls encountered in dbQueryResults",
dbQueryResults.Where(nullableInt => !nullableInt.HasValue).Count());
}
Nullable<T>.GetValueOrDefault example
In this example, we use GetValueOrDefault(UTP) to specify a default that is outside of the expected values returned by dbQueryResults.
private void MapQueryResults()
{
var dbQueryResults = new int?[] { 1, 2, null, 4 };
var nullFlag = int.MinValue;
var ormMap = dbQueryResults.Select(nullableInt => nullableInt.GetValueOrDefault(nullFlag));
// handle nulls
Console.WriteLine("'{0}' indicates a null database value.", nullFlag);
foreach (var num in ormMap)
{
Console.Write("{0} ", num);
}
Console.WriteLine();
}
In this articleIn this section
A System.Linq.Enumerable method is called on an empty collection
The Enumerable methods Aggregate, Average, Last, Max, Min, First, Single, and SingleOrDefault perform operations on a sequence and return a single result.
Some overloads of these methods throw an InvalidOperationException exception when the sequence is empty (other overloads return null (Nothing in Visual Basic). SingleOrDefault also throws InvalidOperationException when the sequence contains more than one element.
Tip
Most of the Enumerable methods disussed in this section are overloaded. Be sure you understand the behavior of the overload that you choose.
Exception messages:
Aggregate, Average, Last, Max, and Min methods | First and FirstOrDefault methods | Single and SingleOrDefault methods
Aggregate, Average, Last, Max, and Min methods
- Additional information: Sequence contains no elements
Causing an InvalidOperationException with Average
In this example, the following method throws an InvalidOperationException when it calls the Average method because the Linq expression returns a sequence that contains no elements that are greater than 4.
private void FindAverageOfNumbersGreaterThan4()
{
var dbQueryResults = new[] { 1, 2, 3, 4 };
var avg = dbQueryResults.Where(num => num > 4).Average();
Console.WriteLine("The average value numbers greater than 4 in the returned records is {0}", avg);
}
When you use one of these methods without checking for an empty sequence, you are implicitly assuming that the sequence is not empty, and that an empty sequence is an unexpected occurrence. Catching or throwing the exception is appropriate when you do assume that the sequence will be non-empty.
Avoiding an InvalidOperationException caused by an empty sequence
Use one of the Enumerable.Any methods to check if the sequence is empty.
Tip
Using Any can improve the performance of the check if the sequence to average might contain a large number of elements or if operation that generates the sequence is expensive.
private void FindAverageOfNumbersGreaterThan4()
{
var dbQueryResults = new[] { 1, 2, 3, 4 };
var moreThan4 = dbQueryResults.Where(num => num > 4);
if(moreThan4.Any())
{
var msgFormat = "The average value numbers greater than 4 in the returned records is {0}";
Console.WriteLine(msgFormat, moreThan4.Average());
}
else
{
// handle empty collection
Console.WriteLine("There are no values greater than 4 in the ecords.");
}
}
In this articleIn this section
First and FirstOrDefault methods
First returns the first item in a sequence or throws an InvalidOperationException if the sequence is empty. You can call the FirstOrDefault method instead of First to return a specified or default value instead of throwing the exception.
Note
In the .NET Framework, types have a concept of default values. For example, for any reference type the default value is null, and for an integer type it is zero. See Default Values Table (C# Reference)
Causing an InvalidOperationException with First
The First is an optimization method that returns the first element in a sequence that satisfies a specified condition. If an element satisfying the condition isn't found, the methods throw an InvalidOperationException exception.
In this example, the First method throws the exception because dbQueryResults doesn't contain an element that is greater than 4.
Exception message:
- Additional information: Sequence contains no matching element
private void FindANumbersGreaterThan4()
{
var dbQueryResults = new[] { 1, 2, 3, 4 };
var firstNum = dbQueryResults.First(n=> n > 4);
var msgFormat = "{0} is an element of dbQueryResults that is greater than 4";
Console.WriteLine(msgFormat, firstNum);
}
Avoiding an exception with FirstOrDefault
You can use one the FirstOrDefault methods in place of First to check that the firstNum sequence contains at least one element. If the query doesn't find an element satisfying the condition, it returns a specified or default value. You can check the returned value to determine if any elements are found.
Note
Using FirstOrDefault can be more complicated to implement if the default value of the type is a valid element in the sequence.
private void FindANumbersGreaterThan4()
{
var dbQueryResults = new[] { 1, 2, 3, 4 };
// default(object) == null
var firstNum = dbQueryResults.FirstOrDefault(n => n > 4);
if (firstNum != 0)
{
var msgFormat = "{0} is an element of dbQueryResults that is greater than 4";
Console.WriteLine(msgFormat, firstNum);
}
else
{
// handle default case
Console.WriteLine("No element of dbQueryResults is greater than 4.");
}
}
In this articleIn this section
Single and SingleOrDefault methods
The Enumerable.Single methods return the only element of a sequence, or the only element of a sequence that meets a specified test.
If there are no elements in the sequence, or there are more than one element in t he sequence, the metnod throws an InvalidOperationException exception.
You can use SingleOrDefault to return a specified or default value instead of throwing the exception when the sequence contains no elements. However, SingleOrDefault still throws an InvalidOperationException when the sequence contains more than one element that matches the selection predicate.
Note
In the .NET Framework, types have a concept of default values. For example, for any reference type the default value is null, and for an integer type it is zero. See Default Values Table (C# Reference)
Causing InvalidOperationExceptions with Single
In this example, singleObject throws an InvalidOperationException because dbQueryResults doesn't contain an element greater than 4.
Exception message:
- Additional information: Sequence contains no matching element
private void FindTheOnlyNumberGreaterThan4()
{
var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };
var singleObject = dbQueryResults.Single(obj => (int)obj > 4);
// display results
var msgFormat = "{0} is the only element of dbQueryResults that is greater than 4";
Console.WriteLine(msgFormat, singleObject);
}
Causing InvalidOperationExceptions with Single or SingleOrDefault
In this example, singleObject throws an InvalidOperationException because dbQueryResults contains more than one element greater than 2.
Exception message:
- Additional information: Sequence contains more than one matching element
private void FindTheOnlyNumberGreaterThan2()
{
var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };
var singleObject = dbQueryResults.SingleOrDefault(obj => (int)obj > 2);
if (singleObject != null)
{
var msgFormat = "{0} is the only element of dbQueryResults that is greater than 2";
Console.WriteLine(msgFormat, singleObject);
}
else
{
// handle empty collection
Console.WriteLine("No element of dbQueryResults is greater than 2.");
}
}
Avoiding InvalidOperationExceptions with Single
Using Single and SingleOrDefault also serves as documentation of your intentions. Single strongly implies that you expect one and only one result to be returned from the condition. SingleOrDefault declares that you expect one or zero results, but no more. When these conditions are invalid, throwing or catching the InvalidOperationException is appropriate. However, if you expect that invalid conditions will occur with some frequency, you should consider using other Enumerable methods, such as First or Where, to generate your results.
During development, you can use one of the Assert methods to check your assumptions. In this example, the highlighted code causes the debugger to break and displays an assert dialog box during development. The assert is removed in release code, and any Single will throw if the results are invalid.
Note
Using Take``1 and setting its count parameter to 2 limits the returned sequence to at most two elements. This sequence includes all the cases that you need to check (0, 1, and more than 1 elements) and can improve the performance of the check when the sequence might contain a large number of elements or if operation that generates the sequence is expensive.
private void FindTheOnlyNumberGreaterThan4()
{
var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };
var moreThan4 = dbQueryResults.Where(obj => (int)obj > 4).Take(2);
System.Diagnostics.Debug.Assert(moreThan4.Count() == 1,
String.Format("moreThan4.Count() == 1; Actual count: {0}", moreThan4.Count()));
// do not handle exceptions in release code
Console.WriteLine("{0} is the only element of dbQueryResults that is greater than 4",
moreThan4.Single());
}
If you want to avoid the exception but still handle the invalid states in release code, you can modify the technique described above. In this example, the method responds to the number of elements returned by moreThan2 in the the switch statement.
private void FindTheOnlyNumberGreaterThan2()
{
var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };
var moreThan2 = dbQueryResults.TakeWhile(obj => (int)obj > 2).Take(2);
switch(moreThan2.Count())
{
case 1:
// success
var msgFormat = "{0} is the only element of dbQueryResults that is greater than 2";
Console.WriteLine(msgFormat, moreThan2.Single());
break;
case 0:
// handle empty collection
Console.WriteLine("No element of the dbQueryResults are greater than 4.");
break;
default: // count > 1
// handle more than one element
Console.WriteLine("More than one element of dbQueryResults is greater than 4");
break;
}
}
In this articleIn this section
Related articles
Design Guidelines for Exceptions (.NET Framework Design Guidelines)
Handling and Throwing Exceptions (.NET Framework Application Essentials)
How to: Receive First-Chance Exception Notifications (.NET Framework Development Guide)
How to: Handle Exceptions in a PLINQ Query (.NET Framework Development Guide)
Exceptions in Managed Threads (.NET Framework Development Guide)
Exceptions and Exception Handling (C# Programming Guide)
Exception Handling Statements (C# Reference)
Try...Catch...Finally Statement (Visual Basic)
Exception Handling (Task Parallel Library)
Exception Handling (Debugging)
Walkthrough: Handling a Concurrency Exception (Accessing Data in Visual Studio)
How to: Handle Errors and Exceptions that Occur with Databinding (Windows Forms)
Handling exceptions in network apps (XAML) (Windows)
In this article