One of the single most frustrating things about debugging async Task<T> functions in Visual Studio is that exceptions occurring upstream, by design, are completely swallowed rather than entering a break state. Let me show you a quick example of how that plays out with async code. In the following example when the AddToQueue method throws an exception we would assume we would enter the break state.

[HttpGet("exception/null-reference-exception/{id}")]
public async Task<IActionResult> SendMessage(string id)
{
	var val = await _context.AddToQueue(); // exception occurs here…
	return Json(val);
}

As you probably know this is not the case. You get no Exception Helper, and no break state. But why?! It is a little involved but here is my quick explanation (I am missing some details).

All async Task<T> functions compile to a state machine with a catch handler, this catches all exceptions thrown in the Task, sets the IsFaulted flag, and adds the exception to the AggregateException in Task.Exception property. The Task is “unwound”, typically when you call await or .Result, and at this point the stored exception is rethrown to the caller as would happen in any synchronous method. Here is the trick though, to a debugger, this exception looks to have already been handled.

To help the debugger identify these scenarios  changes were made in the runtime (.NET 9 specifically) to allow the debugger to indicate that it would like to keep an eye on a particular exception object, so that if the compiled catch handler of a user-code async Task<T> method catches an exception, we continue to be notified about that exception object in case it is rethrown in non-user code.

Now when you are debugging asynchronous code, especially in frameworks like ASP.NET, exceptions thrown across asynchronous boundaries are much easier to deal with. This is in my opinion a really important improvement, especially for .NET Aspire web developers using ASP.NET projects. This has been a long time coming.

To confirm again this only works for .NET 9 and newer projects.

AI generated imag meant to represent Async-state-machine


Comment Section