ErrorLens.ErrorHandling
1.3.1
See the version list below for details.
dotnet add package ErrorLens.ErrorHandling --version 1.3.1
NuGet\Install-Package ErrorLens.ErrorHandling -Version 1.3.1
<PackageReference Include="ErrorLens.ErrorHandling" Version="1.3.1" />
<PackageVersion Include="ErrorLens.ErrorHandling" Version="1.3.1" />
<PackageReference Include="ErrorLens.ErrorHandling" />
paket add ErrorLens.ErrorHandling --version 1.3.1
#r "nuget: ErrorLens.ErrorHandling, 1.3.1"
#:package ErrorLens.ErrorHandling@1.3.1
#addin nuget:?package=ErrorLens.ErrorHandling&version=1.3.1
#tool nuget:?package=ErrorLens.ErrorHandling&version=1.3.1
ErrorLens.ErrorHandling
Transform unhandled exceptions into clean, structured JSON responses with minimal setup — just two lines of code to get started.
ErrorLens provides a production-ready error handling pipeline for ASP.NET Core APIs. Every exception is automatically converted into a consistent, structured JSON response with appropriate HTTP status codes, machine-readable error codes, and human-readable messages. The library handles the full lifecycle from exception to response: handler selection, response customization, secure 5xx safe messaging, configurable logging, and optional localization — with zero-config defaults that work out of the box and deep extensibility when you need it.
Full documentation: https://ahmedv20.github.io/error-handling-dotnet/current/
Features
- Zero-Config Exception Handling — Unhandled exceptions return structured JSON error responses out of the box
- Validation Error Details — Field-level errors with property names, messages, and rejected values
- Secure Error Responses — 5xx errors return generic safe messages to prevent information disclosure
- Custom JSON Field Names — Rename any response field (
code→type,message→detail, etc.) - YAML & JSON Configuration — Configure error codes, messages, HTTP statuses via
appsettings.jsonorerrorhandling.yml - Custom Exception Attributes —
[ResponseErrorCode],[ResponseStatus],[ResponseErrorProperty] - Custom Exception Handlers — Register
IApiExceptionHandlerimplementations with priority ordering - AggregateException Unwrapping — Automatically flattens and unwraps single-inner
AggregateException - Response Customization — Add global properties (traceId, timestamp) via
IApiErrorResponseCustomizer - Replaceable Mappers — Override
IErrorCodeMapper,IErrorMessageMapper, orIHttpStatusMapper - RFC 9457 Problem Details — Opt-in
application/problem+jsoncompliant responses - Configurable Logging — Control log levels and stack trace verbosity per HTTP status code
- Startup Validation — JSON field names validated at startup (non-null, non-empty, unique)
- Multi-Target Support — .NET 6.0, 7.0, 8.0, 9.0, and 10.0
- .NET 8+ Native Integration — Automatically registers
IExceptionHandleron .NET 8+; falls back to middleware on .NET 6/7 - OpenTelemetry Tracing — Automatic
Activityspans with error tags and OTel semantic conventions - Error Message Localization —
IErrorMessageLocalizerwithIStringLocalizerbridge for multi-language support - OpenAPI / Swagger Integration — Auto-add error response schemas to API docs
- Rate Limiting — Structured 429 responses with
Retry-Afterheaders viaIRateLimitResponseWriter(.NET 7+) - Built-in Error Code Constants —
DefaultErrorCodesclass with 23 predefined codes for consistent frontend matching
Quick Start
Installation
dotnet add package ErrorLens.ErrorHandling
Minimal API (.NET 6+)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddErrorHandling();
var app = builder.Build();
app.UseErrorHandling();
app.MapGet("/", () => { throw new Exception("Something went wrong"); });
app.Run();
Controller-Based API
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddErrorHandling();
var app = builder.Build();
app.UseErrorHandling();
app.MapControllers();
app.Run();
With Configuration Options
// Option 1: Inline options
builder.Services.AddErrorHandling(options =>
{
options.HttpStatusInJsonResponse = true;
options.OverrideModelStateValidation = true;
options.IncludeRejectedValues = true;
options.ExceptionLogging = ExceptionLogging.WithStacktrace;
});
// Option 2: Bind from appsettings.json / YAML
builder.Services.AddErrorHandling(builder.Configuration);
Note (.NET 8+): ErrorLens automatically registers
IExceptionHandleron .NET 8+, so exceptions are handled natively by the ASP.NET Core exception handler pipeline. On .NET 6/7,UseErrorHandling()registers middleware instead. Both paths produce identical results.
All unhandled exceptions now return structured JSON:
{
"code": "INVALID_OPERATION",
"message": "The operation is not valid."
}
The error code is generated automatically from the exception class name using ALL_CAPS strategy — InvalidOperationException becomes INVALID_OPERATION, UserNotFoundException becomes USER_NOT_FOUND.
How It Works
ErrorLens processes exceptions through a pipeline with clearly defined stages:
Exception thrown
→ Handler Selection (sorted by Order, first CanHandle() match wins)
→ Fallback Handler (if no handler matches)
→ HTTP Status in JSON (if configured)
→ Response Customizers (all IApiErrorResponseCustomizer run in order)
→ Logging (ILoggingService with ILoggingFilter checks)
→ Localization (IErrorMessageLocalizer replaces messages)
→ OpenTelemetry (Activity enriched with error tags)
→ JSON Response (or Problem Details if enabled)
Each stage is independently configurable and replaceable. If any handler or customizer throws, the pipeline catches the error, logs both exceptions, and returns a safe 500 response to prevent cascading failures.
For a detailed architecture overview with Mermaid diagrams, see docs/ARCHITECTURE.md.
Packages
| Package | Description | Target Frameworks | Version |
|---|---|---|---|
| ErrorLens.ErrorHandling | Core middleware for structured error responses | .NET 6, 7, 8, 9, 10 | |
| ErrorLens.ErrorHandling.OpenApi | OpenAPI schema generation (.NET 9+) | .NET 9, 10 | |
| ErrorLens.ErrorHandling.Swashbuckle | Swashbuckle integration (.NET 6-8) | .NET 6, 7, 8 |
# Optional integration packages
dotnet add package ErrorLens.ErrorHandling.OpenApi # .NET 9+
dotnet add package ErrorLens.ErrorHandling.Swashbuckle # .NET 6-8
Default HTTP Status Mappings
ErrorLens maps common .NET exception types to appropriate HTTP status codes out of the box:
| Exception Type | HTTP Status |
|---|---|
ArgumentException / ArgumentNullException |
400 Bad Request |
InvalidOperationException |
400 Bad Request |
FormatException |
400 Bad Request |
UnauthorizedAccessException |
401 Unauthorized |
KeyNotFoundException |
404 Not Found |
FileNotFoundException / DirectoryNotFoundException |
404 Not Found |
TimeoutException |
408 Request Timeout |
OperationCanceledException |
499 Client Closed Request |
NotImplementedException |
501 Not Implemented |
| All others | 500 Internal Server Error |
Note:
TaskCanceledExceptioninherits fromOperationCanceledException, so it also maps to 499 automatically.
Override any mapping via configuration or [ResponseStatus] attributes.
Configuration
ErrorLens supports both JSON (appsettings.json) and YAML (errorhandling.yml) configuration. Here is a minimal example:
{
"ErrorHandling": {
"HttpStatusInJsonResponse": true,
"OverrideModelStateValidation": true,
"IncludeRejectedValues": true,
"ExceptionLogging": "WithStacktrace",
"HttpStatuses": {
"MyApp.UserNotFoundException": 404
},
"Codes": {
"MyApp.UserNotFoundException": "USER_NOT_FOUND"
}
}
}
For YAML configuration:
ErrorHandling:
HttpStatusInJsonResponse: true
OverrideModelStateValidation: true
IncludeRejectedValues: true
ExceptionLogging: WithStacktrace
HttpStatuses:
MyApp.UserNotFoundException: 404
Codes:
MyApp.UserNotFoundException: USER_NOT_FOUND
builder.Configuration.AddYamlErrorHandling("errorhandling.yml");
builder.Services.AddErrorHandling(builder.Configuration);
Settings are resolved in this order (highest priority first):
- Custom exception handlers —
IApiExceptionHandlerimplementations - Inline options —
Action<ErrorHandlingOptions>inAddErrorHandling() - Configuration binding —
appsettings.jsonorerrorhandling.yml - Exception attributes —
[ResponseErrorCode],[ResponseStatus] - Default conventions — class name to
ALL_CAPS, built-in HTTP status mappings
For the full configuration reference (all options, JSON field names, rate limiting, OpenAPI), see the Configuration Guide.
Exception Attributes
Decorate exception classes with attributes to control error responses declaratively:
[ResponseErrorCode("USER_NOT_FOUND")]
[ResponseStatus(HttpStatusCode.NotFound)]
public class UserNotFoundException : Exception
{
[ResponseErrorProperty("userId")]
public string UserId { get; }
public UserNotFoundException(string userId)
: base($"User {userId} not found") => UserId = userId;
}
Response:
{
"code": "USER_NOT_FOUND",
"message": "User abc-123 not found",
"userId": "abc-123"
}
| Attribute | Target | Description |
|---|---|---|
[ResponseErrorCode("CODE")] |
Class | Sets a custom error code |
[ResponseStatus(HttpStatusCode.NotFound)] |
Class | Sets the HTTP status code (accepts HttpStatusCode enum) |
[ResponseStatus(404)] |
Class | Sets the HTTP status code (accepts int, must be 100-599) |
[ResponseErrorProperty("name")] |
Property | Includes the property in the JSON response |
For more details, see Exception Attributes.
Validation Errors
Enable OverrideModelStateValidation to get structured field-level validation errors:
builder.Services.AddErrorHandling(options =>
{
options.OverrideModelStateValidation = true;
});
Response:
{
"code": "VALIDATION_FAILED",
"message": "Validation failed",
"fieldErrors": [
{
"code": "INVALID_EMAIL",
"property": "email",
"message": "Invalid email format",
"rejectedValue": "bad",
"path": "email"
}
]
}
Validation error codes and messages can be customized via configuration. See the Configuration Guide for details.
Security
All 5xx-class errors (500-599) automatically return a generic safe message instead of the raw exception message:
{
"code": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}
This prevents internal details (database connection strings, file paths, stack traces) from leaking to API consumers. The original exception is still logged with full details on the server side. The BadRequestExceptionHandler also sanitizes Kestrel-internal error messages automatically.
Samples
| Sample | Description | Key Features |
|---|---|---|
MinimalApiSample |
Zero-config minimal API | Default error handling, automatic error codes |
FullApiSample |
Controller-based API with extensibility | Custom handlers, response customizers, logging filters, Swagger |
ShowcaseSample |
Full feature showcase with YAML | YAML config, JSON field names, attributes, Problem Details, Swashbuckle |
IntegrationSample |
Modern .NET ecosystem integration | OpenTelemetry, localization, OpenAPI, rate limiting |
Documentation
Documentation site: https://ahmedv20.github.io/error-handling-dotnet/current/
Guides
- Getting Started — Installation, setup, first error response
- Configuration — JSON/YAML config, all options, priority order
- Logging — Log levels, stack traces, logging filters
- Troubleshooting — Common issues and solutions
Core Features
- Exception Attributes —
[ResponseErrorCode],[ResponseStatus],[ResponseErrorProperty] - Custom Handlers —
IApiExceptionHandler,IFallbackApiExceptionHandler - Response Customization —
IApiErrorResponseCustomizer - JSON Field Names — Rename any response field
- Problem Details (RFC 9457) —
application/problem+jsonformat
Integration Features
- OpenTelemetry Tracing — Automatic
Activityspans - Localization — Multi-language error messages
- OpenAPI (.NET 9+) — Auto-generated error schemas
- Swashbuckle (.NET 6-8) — Swagger error schemas
- Rate Limiting (.NET 7+) — Structured 429 responses
Reference
- Architecture — Full architecture guide with Mermaid diagrams
- API Reference — All public types, interfaces, and extension methods
- Changelog — Version history
Prerequisites
- .NET SDK 6.0 or later (multi-targeting builds require .NET 10.0 SDK)
- ASP.NET Core application (Minimal API or Controller-based)
Building from Source
git clone https://github.com/AhmedV20/error-handling-dotnet.git
cd error-handling-dotnet
dotnet restore
dotnet build
Running Tests
dotnet test
Running Sample Projects
dotnet run --project samples/MinimalApiSample
dotnet run --project samples/ShowcaseSample
🤝 Contributing
See CONTRIBUTING.md for guidelines.
License
MIT License — see LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.OpenApi (>= 1.2.3)
- NetEscapades.Configuration.Yaml (>= 3.1.0)
-
net6.0
- Microsoft.Extensions.Localization.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- Microsoft.OpenApi (>= 1.2.3)
- NetEscapades.Configuration.Yaml (>= 3.1.0)
-
net7.0
- Microsoft.Extensions.Localization.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- Microsoft.OpenApi (>= 1.2.3)
- NetEscapades.Configuration.Yaml (>= 3.1.0)
- System.Threading.RateLimiting (>= 8.0.0)
-
net8.0
- Microsoft.OpenApi (>= 1.2.3)
- NetEscapades.Configuration.Yaml (>= 3.1.0)
-
net9.0
- Microsoft.OpenApi (>= 1.2.3)
- NetEscapades.Configuration.Yaml (>= 3.1.0)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on ErrorLens.ErrorHandling:
| Package | Downloads |
|---|---|
|
ErrorLens.ErrorHandling.OpenApi
Microsoft.AspNetCore.OpenApi integration for ErrorLens.ErrorHandling — automatically adds error response schemas to your OpenAPI documentation (.NET 9+). |
|
|
ErrorLens.ErrorHandling.Swashbuckle
Swashbuckle integration for ErrorLens.ErrorHandling — automatically adds error response schemas to your Swagger/OpenAPI documentation. |
|
|
ErrorLens.ErrorHandling.FluentValidation
FluentValidation integration for ErrorLens.ErrorHandling — automatically catches FluentValidation.ValidationException and maps failures to structured API error responses with field errors, error codes, and rejected values. |
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.3.1:
- IncludeRejectedValues option to suppress sensitive data in validation errors
- Pipeline resilience: localization/telemetry errors no longer crash error handling
- OperationCanceledException propagates naturally instead of returning 500
- Field-specific localization with composite key support (fieldName.errorCode)
- ProblemDetails uses configured JsonFieldNames instead of hardcoded values
- Rate limiting header and retry-after rounding fixes
- Null guards for model constructors, case-insensitive field name validation
v1.3.0:
- OpenTelemetry distributed tracing via System.Diagnostics.Activity
- Localization support with IErrorMessageLocalizer and IStringLocalizer bridge
- New package: ErrorLens.ErrorHandling.OpenApi (.NET 9+ OpenAPI integration)
- New package: ErrorLens.ErrorHandling.Swashbuckle (Swagger integration for .NET 6-8)
- Rate limiting integration with IRateLimitResponseWriter
- AggregateException handler
- Options validation at startup
- Idempotent DI registration for custom handlers and customizers
v1.1.1:
- Fix: Register ILoggingService in DI container (logging was not working in v1.1.0)
v1.1.0:
- Custom JSON field names (rename code→type, message→detail, etc.)
- YAML configuration support via AddYamlErrorHandling()
- OverrideModelStateValidation option for [ApiController] validation interception
- ModelStateValidationExceptionHandler with structured fieldErrors format
- Professional documentation with AsciiDoc
- 175 tests (up from 142)
Full changelog: https://github.com/AhmedV20/error-handling-dotnet/blob/main/CHANGELOG.md
