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

Exception Handling: async Task vs async void async Task Exception wrapped in Task Can be caught with await Structured exception handling ? Recommended async void Exception thrown directly Cannot be caught normally Crashes application ? Avoid except events

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.

Updated on: 2026-03-17T07:04:36+05:30

983 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements