CodeBlanks Logo Icon CodeBlanks

Throw timing

C# course · Advanced · Lesson 16/83

Learn throw behavior before and after await.

Throw timing

A throw statement inside an async method does not fire immediately. The error is stored in the returned Task; it is rethrown when you await it. So var t = ThrowAsync(); does not throw; await t; does.

This requires care in fire-and-forget scenarios: writing ThrowAsync(); and ignoring it leaves the error in the Task, silently ignored. To catch the error, await the Task or check its Exception property.

When the exception fires

Knowing when the error fires is critical for debugging. An async method runs synchronously up to the first await. If a throw comes after an await, when the throw runs the error is stored in the Task.

The remaining code (after throw) does not run. If the caller awaits the Task, the throw fires; if not, it is silently ignored. So always await or monitor the Task.

Exercise Throw and catch

Define static async Task ThrowIfNegative(int n). In its body call await Task.CompletedTask;. If n < 0 throw new ArgumentException("Negative");. Make Main a static async Task Main. Catch the error with try { await ThrowIfNegative(-1); } catch (ArgumentException ex) { Console.WriteLine(ex.Message); } and print the message.

Show example solution
In ThrowIfNegative body write await Task.CompletedTask; and if (n < 0) throw new ArgumentException("Negative");. Make Main static async Task Main() and write try { await ThrowIfNegative(-1); } catch (ArgumentException ex) { Console.WriteLine(ex.Message); }
Example solution:
using System;
using System.Threading.Tasks;

class Program
{
    static async Task ThrowIfNegative(int n)
    {
        await Task.CompletedTask;
        if (n < 0)
        {
            throw new ArgumentException("Negative");
        }
    }

    static async Task Main()
    {
        try
        {
            await ThrowIfNegative(-1);
        }
        catch (ArgumentException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

Test yourself

  1. Match throw timing

    var t = ThrowAsync();
    Error not yet thrown
    await t;
    Error is thrown
    throw new Exception();
    Stores error in Task
    t.Exception
    Inspects the error
  2. When does throw fire in an async method?

    • At once, on the throw line
    • When Main starts
    • When awaited
    • When Task.Dispose is called
    Show answer

    When awaited — Correct. In async methods, throw fires when awaited.

  3. Complete the throw statement

    static async Task Fail() { ____ new InvalidOperationException("Hata"); }
    Show answer
    static async Task Fail() { throw new InvalidOperationException("Hata"); }

All lessons in Advanced