C# Async Programming: Task, async/await & Cancellation

Lesson 5 of 7

Exception Handling in Async Code

Exceptions in async code behave a little differently from synchronous code, especially once multiple tasks are involved.

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        try
        {
            await RiskyAsync();
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine($"Caught: {ex.Message}");
        }
    }

    static async Task RiskyAsync()
    {
        await Task.Delay(500);
        throw new InvalidOperationException("Something went wrong");
    }
}

The pieces

  • A regular try/catch around an await works exactly like you'd hope, an exception thrown inside the awaited async method surfaces at the await point, as if it were a normal synchronous call.
  • With Task.WhenAll, if multiple tasks fail, only the first exception is rethrown by the await, the rest are still available on each individual Task.Exception property (an AggregateException) if you need them.
Task t1 = FailAsync("A");
Task t2 = FailAsync("B");

try
{
    await Task.WhenAll(t1, t2);
}
catch
{
    Console.WriteLine(t1.Exception?.InnerException?.Message);
    Console.WriteLine(t2.Exception?.InnerException?.Message);
}
  • An exception thrown inside an async void method (instead of async Task) can't be caught by a surrounding try/catch at all, it escapes straight to the app's global unhandled-exception handler, another reason to avoid async void outside of event handlers.

Best practice

Catch specific exception types close to where they can meaningfully be handled, exactly like synchronous code, async/await doesn't change the fundamentals of exception handling, it just changes when the exception surfaces (at the await, not at the original call).

📝 Async Exceptions Quiz

Passing score: 70%
  1. 1.Where does an exception thrown inside an awaited async method surface?

  2. 2.An exception thrown inside an async ____ method cannot be caught by a surrounding try/catch.

  3. 3.When multiple tasks passed to Task.WhenAll fail, only the first exception is rethrown by the await, though the rest remain available on each Task.