Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
How to catch an exception thrown by an async void method in C#?
In synchronous C# code, exceptions propagate up the call stack until they reach an appropriate catch block. However, exception handling in asynchronous methods behaves differently depending on the return type.
An async method in C# can have three return types: void, Task, and Task<T>. When an exception occurs in an async method with Task or Task<T> return type, the exception is wrapped in an AggregateException and attached to the Task object. However, async void methods behave differently and present unique challenges for exception handling.
Async Task vs Async Void Exception Handling
Catching Exceptions from Async Task Methods
When using async Task, exceptions can be caught normally using try-catch blocks −
Example
using System;
using System.Threading.Tasks;
class Program {
static async Task Main(string[] args) {
await DoSomething();
}
public static async Task ThrowException() {
throw new ArgumentNullException("Parameter cannot be null");
}
public static async Task DoSomething() {
try {
await ThrowException();
}
catch (ArgumentNullException ex) {
Console.WriteLine("Caught exception: " + ex.Message);
}
}
}
The output of the above code is −
Caught exception: Parameter cannot be null
The Problem with Async Void Methods
Unlike async Task methods, exceptions thrown by async void methods cannot be caught by the calling code. These exceptions are posted directly to the SynchronizationContext and can crash the application −
Example - Uncatchable Exception
using System;
using System.Threading.Tasks;
class Program {
static async Task Main(string[] args) {
try {
AsyncVoidMethod();
await Task.Delay(100); // Wait for async void to complete
}
catch (Exception ex) {
Console.WriteLine("This will NOT catch the exception: " + ex.Message);
}
Console.WriteLine("Program continues but exception was unhandled");
}
public static async void AsyncVoidMethod() {
await Task.Delay(50);
throw new InvalidOperationException("Exception from async void method");
}
}
The output of the above code is −
Program continues but exception was unhandled
Solutions for Handling Async Void Exceptions
Solution 1: Use Async Task Instead
The best solution is to avoid async void and use async Task instead −
using System;
using System.Threading.Tasks;
class Program {
static async Task Main(string[] args) {
try {
await AsyncTaskMethod();
}
catch (Exception ex) {
Console.WriteLine("Successfully caught: " + ex.Message);
}
}
public static async Task AsyncTaskMethod() {
await Task.Delay(50);
throw new InvalidOperationException("Exception from async Task method");
}
}
The output of the above code is −
Successfully caught: Exception from async Task method
Solution 2: Handle Exceptions Inside Async Void
If you must use async void (such as for event handlers), handle exceptions internally −
using System;
using System.Threading.Tasks;
class Program {
static async Task Main(string[] args) {
AsyncVoidWithHandling();
await Task.Delay(200); // Wait for completion
}
public static async void AsyncVoidWithHandling() {
try {
await Task.Delay(50);
throw new InvalidOperationException("Internal exception");
}
catch (Exception ex) {
Console.WriteLine("Handled internally: " + ex.Message);
}
}
}
The output of the above code is −
Handled internally: Internal exception
When to Use Async Void
| Use Case | Return Type | Exception Handling |
|---|---|---|
| Event handlers | async void | Handle internally with try-catch |
| Fire-and-forget operations | async Task | Can be awaited and caught |
| Regular async methods | async Task | Standard try-catch with await |
Conclusion
Exceptions from async void methods cannot be caught by normal try-catch blocks and are posted directly to the SynchronizationContext. The best practice is to use async Task instead, which allows proper exception handling through await and try-catch blocks. Use async void only for event handlers and always handle exceptions internally.
