Skip to content
34 changes: 31 additions & 3 deletions aspnetcore/fundamentals/error-handling.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
---
title: Handle errors in ASP.NET Core
ai-usage: ai-assisted
author: tdykstra
description: Discover how to handle errors in ASP.NET Core apps.
monikerRange: '>= aspnetcore-3.1'
ms.author: tdykstra
ms.custom: mvc
ms.date: 01/15/2025
ms.date: 09/25/2025
uid: fundamentals/error-handling
---
# Handle errors in ASP.NET Core
Expand Down Expand Up @@ -79,11 +80,13 @@ Another way to use a lambda is to set the status code based on the exception typ

## IExceptionHandler

[IExceptionHandler](/dotnet/api/microsoft.aspnetcore.diagnostics.iexceptionhandler) is an interface that gives the developer a callback for handling known exceptions in a central location.
[IExceptionHandler](/dotnet/api/microsoft.aspnetcore.diagnostics.iexceptionhandler) is an interface that gives the developer a callback for handling known exceptions in a central location. The interface contains a single method, [`TryHandleAsync`](/dotnet/api/microsoft.aspnetcore.diagnostics.iexceptionhandler.tryhandleasync), which receives an `HttpContext` and an `Exception` parameter.

`IExceptionHandler` implementations are registered by calling [`IServiceCollection.AddExceptionHandler<T>`](/dotnet/api/microsoft.extensions.dependencyinjection.exceptionhandlerservicecollectionextensions.addexceptionhandler). The lifetime of an `IExceptionHandler` instance is singleton. Multiple implementations can be added, and they're called in the order registered.

If an exception handler handles a request, it can return `true` to stop processing. If an exception isn't handled by any exception handler, then control falls back to the default behavior and options from the middleware. Different metrics and logs are emitted for handled versus unhandled exceptions.
The built-in implementation works by iterating through the registered exception handlers in order until one returns `true` from `TryHandleAsync`, indicating that the exception has been handled. If an exception handler handles an exception, it can return `true` to stop processing. If an exception isn't handled by any exception handler, then control falls back to the default behavior and options from the middleware.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot

The built-in implementation works by iterating through the registered exception handlers

The built-in implementation of what? I don't think this is clear.

I think it should be rephrased to something like:

Exception handling middleware interates through registered exception handlers...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to clarify that it's the exception handling middleware that iterates through registered exception handlers. See commit 2c8a4e5.


Starting in .NET 10, the default behavior is to suppress emission of diagnostics such as logs and metrics for handled exceptions (when `TryHandleAsync` returns `true`). This differs from earlier versions (.NET 8 and 9) where diagnostics were always emitted regardless of whether the exception was handled. The default behavior can be changed by setting [SuppressDiagnosticsCallback](#suppressdiagnosticscallback).

The following example shows an `IExceptionHandler` implementation:

Expand All @@ -103,6 +106,30 @@ In other environments:
* The `CustomExceptionHandler` is called first to handle an exception.
* After logging the exception, the `TryHandleAsync` method returns `false`, so the [`/Error` page](#exception-handler-page) is shown.

### SuppressDiagnosticsCallback

Starting in .NET 10, you can control whether the exception handling middleware writes diagnostics for handled exceptions by configuring the `SuppressDiagnosticsCallback` property on `ExceptionHandlerOptions`. This callback receives the exception context and allows you to determine whether diagnostics should be suppressed based on the specific exception or request.

To revert to the .NET 8 and 9 behavior where diagnostics are always emitted for handled exceptions, set the callback to always return `false`:

```csharp
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => false
});
```

You can also conditionally suppress diagnostics based on the exception type or other context:

```csharp
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => context.Exception is ArgumentException
});
```

When an exception isn't handled by any `IExceptionHandler` implementation (all handlers return `false` from `TryHandleAsync`), control falls back to the default behavior and options from the middleware, and diagnostics are emitted according to the middleware's standard behavior.

<!-- links to this in other docs require sestatuscodepages -->
<a name="sestatuscodepages"></a>

Expand Down Expand Up @@ -343,6 +370,7 @@ An alternative approach to generate problem details is to use the third-party Nu
* <xref:test/troubleshoot-azure-iis>
* <xref:host-and-deploy/azure-iis-errors-reference>
* <xref:fundamentals/error-handling-api>
* <xref:Microsoft.AspNetCore.Builder.ExceptionHandlerOptions.SuppressDiagnosticsCallback?displayProperty=nameWithType>
* [Breaking change: Exception diagnostics are suppressed when `IExceptionHandler.TryHandleAsync` returns true](https://github.com/aspnet/Announcements/issues/524)

:::moniker-end
Expand Down