Published on

Exception Handling: Beyond Try/Catch

Authors

Rethinking the Basics

try/catch is what you would normally reach for when handling an exception, but there is much more to it. One of the biggest pitfalls is swallowing exceptions, which unfortunatley is much more common that you think. The big issue here is that it makes debugging much harder. Furthermore it hides real problems, which leads to issues that is experienced, but not discovered in logs etc.

Exception Filters

In my previous post, I showed how filters let you conditionally catch exceptions:

catch (Exception ex) when (ex is InvalidOperationException)

Which is much cleaner than re-throwing. It also keeps the intent much more visible.

Handling Multiple Exceptions with AggregateException

When multiple tasks fail (e.g., Task.WhenAll), .NET throws an AggregateException.
What essentially hapepns is, that you instead of a single failyre, we get a list of exceptions:

try
{
    await Task.WhenAll(tasks);
}
catch (AggregateException ex)
{
    foreach (var inner in ex.Flatten().InnerExceptions)
    {
        Console.WriteLine(inner.Message);
    }
}

This ensures that all exceptions are handled and uncovered

Async Exception Handling

With async Task methods, you will see exceptions pop up when you await them. But async void? That's a different story. Exceptions there tend to bypass your try/catch blocks and head straight to the application's core, often causing a crash.

It is a good idea to stick to async Task.

Uh Oh, Global Exception Handling

Sometimes real stuff breaks. Global exception handling is the safety net for unexpected moments.

  • ASP.NET Core: Gives us the UseExceptionHandler middleware. Think of it as your emergency fallback page.

  • Console Apps: Gives you the option to hook into AppDomain.CurrentDomain.UnhandledException to catch things that slips through the cracks.

This will not save a crashing app. This is mostly for logging and cleanup, before the walls crashes down.

A Few Things I Stick To

  • Exceptions are not control flow (The path the program runs). DO NOT abuse them that way.
  • If the domain needs its own exceptions, give them names and use them.
  • Logging is only useful if you add some context. Otherwise it is just noise, which is ignored.
  • Only catch what you actually intend to handle.

To summarize

Exceptions are not just about try/catch.
With filters, AggregateException, async quirks and global handlers, you can make failure handling predictable, easier to debug, and something you actually want to maintain.