Exception filters

C# 6.0 NETFx 4.6

Published Updated Author Jeffrey T. Fritz Reading time

Exception filters let you catch only the failures you actually know how to handle.

Exception filters add a when clause to catch, so the runtime evaluates a condition before it commits to that handler. That makes the catch block more honest: it runs only for the specific state you expect, while other failures continue searching for a better handler.

This is especially useful for transient errors, status-code-specific recovery, and lightweight logging where you want more precision than a broad catch followed by if statements.

Why it matters

Without filters, code often catches a broad exception, inspects it inside the handler, and then rethrows if the details do not match. Filters move that decision into the catch declaration itself, which keeps the recovery path smaller and makes the intent obvious.

They also preserve useful debugging behavior because the runtime has not fully unwound into the handler unless the filter passes.

Cautions

Keep filter expressions predictable. If a filter throws while being evaluated, the runtime treats the filter as failed and continues looking for another handler.

That means filters are best for checks and lightweight logging, not for work that mutates state, allocates heavily, or depends on fragile helper code.

Catch only the cases you can recover from

A `when` clause keeps the recovery path narrow instead of over-catching and branching inside the handler.

Valid since C# 6.0

using System;

public sealed class ApiException : Exception
{
    public ApiException(int statusCode, string message) : base(message)
    {
        StatusCode = statusCode;
    }

    public int StatusCode { get; }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine(ReadProfile());
    }

    private static string ReadProfile()
    {
        try
        {
            throw new ApiException(404, "Profile was not found.");
        }
        catch (ApiException ex) when (ex.StatusCode == 404)
        {
            return "Use cached profile until the user signs in again.";
        }
    }
}

Log during the filter and keep bubbling

Filters can record context before the runtime commits to a handler, letting the original exception continue upward unchanged.

Valid since C# 6.0

using System;

public static class Program
{
    public static void Main()
    {
        try
        {
            try
            {
                throw new InvalidOperationException("Disk quota reached.");
            }
            catch (Exception ex) when (Log(ex, "nightly import"))
            {
                Console.WriteLine("This block never runs because the filter returned false.");
            }
        }
        catch (InvalidOperationException)
        {
            Console.WriteLine("The original exception keeps bubbling after logging.");
        }
    }

    private static bool Log(Exception ex, string operation)
    {
        Console.WriteLine(string.Format(
            "Logging {0}: {1} - {2}",
            operation,
            ex.GetType().Name,
            ex.Message));
        return false;
    }
}

Learn more

Exception handling statements (Microsoft Learn)