ErrorLens.ErrorHandling
1.3.0
See the version list below for details.
dotnet add package ErrorLens.ErrorHandling --version 1.3.0
NuGet\Install-Package ErrorLens.ErrorHandling -Version 1.3.0
<PackageReference Include="ErrorLens.ErrorHandling" Version="1.3.0" />
<PackageVersion Include="ErrorLens.ErrorHandling" Version="1.3.0" />
<PackageReference Include="ErrorLens.ErrorHandling" />
paket add ErrorLens.ErrorHandling --version 1.3.0
#r "nuget: ErrorLens.ErrorHandling, 1.3.0"
#:package ErrorLens.ErrorHandling@1.3.0
#addin nuget:?package=ErrorLens.ErrorHandling&version=1.3.0
#tool nuget:?package=ErrorLens.ErrorHandling&version=1.3.0
ErrorLens.ErrorHandling
Transform unhandled exceptions into clean, structured JSON responses with zero configuration required — just two lines of code and you're ready for production. As your needs evolve, take full control with declarative attributes on your exception classes, comprehensive YAML/JSON configuration files for global settings, custom error handlers for complex transformation logic, validation error mapping, multi-language localization support, OpenTelemetry tracing integration, and RFC 9457 Problem Details compliance — all while maintaining automatic sensitive data sanitization and comprehensive logging.
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 (aggregate unwrapping built-in) - Response Customization — Add global properties (traceId, timestamp) via
IApiErrorResponseCustomizer - 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
- OpenTelemetry Tracing — Automatic
Activityspans with error tags and OTel semantic conventions (zero dependencies) - Error Message Localization —
IErrorMessageLocalizerwithIStringLocalizerbridge for multi-language error messages - OpenAPI Schema Generation — Auto-add error response schemas to .NET 9+ OpenAPI docs (
ErrorLens.ErrorHandling.OpenApi) - Swashbuckle Integration — Auto-add error response schemas to Swagger docs for .NET 6-8 (
ErrorLens.ErrorHandling.Swashbuckle) - Rate Limiting — Structured 429 responses with
Retry-Afterheaders viaIRateLimitResponseWriter(.NET 7+)
📦 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 |
Installation
dotnet add package ErrorLens.ErrorHandling
Optional Integration Packages
# OpenAPI schema generation for .NET 9+ (Microsoft.AspNetCore.OpenApi)
dotnet add package ErrorLens.ErrorHandling.OpenApi
# Swagger schema generation for .NET 6-8 (Swashbuckle)
dotnet add package ErrorLens.ErrorHandling.Swashbuckle
Quick Start
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddErrorHandling();
var app = builder.Build();
app.UseErrorHandling();
app.MapControllers();
app.Run();
All unhandled exceptions now return structured JSON:
{
"code": "INVALID_OPERATION",
"message": "The operation is not valid."
}
Configuration
JSON (appsettings.json)
{
"ErrorHandling": {
"Enabled": true,
"HttpStatusInJsonResponse": true,
"DefaultErrorCodeStrategy": "AllCaps",
"AddPathToError": true,
"OverrideModelStateValidation": true,
"SearchSuperClassHierarchy": true,
"ExceptionLogging": "WithStacktrace",
"JsonFieldNames": {
"Code": "type",
"Message": "detail"
},
"HttpStatuses": {
"MyApp.UserNotFoundException": 404,
"MyApp.DuplicateEmailException": 409
},
"Codes": {
"MyApp.UserNotFoundException": "USER_NOT_FOUND",
"email.Required": "EMAIL_REQUIRED"
},
"Messages": {
"MyApp.UserNotFoundException": "The requested user was not found"
},
"LogLevels": {
"4xx": "Warning",
"5xx": "Error",
"404": "Debug"
}
}
}
YAML (errorhandling.yml)
ErrorHandling:
Enabled: true
HttpStatusInJsonResponse: true
DefaultErrorCodeStrategy: AllCaps
AddPathToError: true
OverrideModelStateValidation: true
SearchSuperClassHierarchy: true
ExceptionLogging: WithStacktrace
JsonFieldNames:
Code: type
Message: detail
HttpStatuses:
MyApp.UserNotFoundException: 404
MyApp.DuplicateEmailException: 409
Codes:
MyApp.UserNotFoundException: USER_NOT_FOUND
email.Required: EMAIL_REQUIRED
Messages:
MyApp.UserNotFoundException: The requested user was not found
LogLevels:
4xx: Warning
5xx: Error
404: Debug
To use YAML configuration:
builder.Configuration.AddYamlErrorHandling("errorhandling.yml");
builder.Services.AddErrorHandling(builder.Configuration);
A full YAML template with all options is available at docs/errorhandling-template.yml.
Custom JSON Field Names
Rename any JSON property in error responses to match your API conventions:
ErrorHandling:
JsonFieldNames:
Code: type # "code" → "type"
Message: detail # "message" → "detail"
Status: statusCode # "status" → "statusCode"
FieldErrors: fields # "fieldErrors" → "fields"
Property: field # "property" → "field"
Before:
{ "code": "USER_NOT_FOUND", "message": "User not found" }
After:
{ "type": "USER_NOT_FOUND", "detail": "User not found" }
Custom Exception Attributes
Decorate exception classes with attributes to control error responses:
[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"
}
Custom Exception Handlers
Register custom handlers for specialized exception types:
public class InfrastructureExceptionHandler : IApiExceptionHandler
{
public int Order => 50; // Lower = higher priority (runs before built-in handlers at 100+)
public bool CanHandle(Exception ex) => ex is DatabaseTimeoutException;
public ApiErrorResponse Handle(Exception ex) =>
new(HttpStatusCode.ServiceUnavailable, "DATABASE_TIMEOUT", ex.Message);
}
// Register
builder.Services.AddApiExceptionHandler<InfrastructureExceptionHandler>();
Response Customization
Add global properties to all error responses:
public class TraceIdCustomizer : IApiErrorResponseCustomizer
{
public void Customize(ApiErrorResponse response)
{
response.AddProperty("traceId", Activity.Current?.Id);
response.AddProperty("timestamp", DateTime.UtcNow.ToString("o"));
}
}
// Register
builder.Services.AddErrorResponseCustomizer<TraceIdCustomizer>();
Validation Errors
Validation exceptions automatically include field-level details. Enable OverrideModelStateValidation: true to intercept [ApiController] automatic validation and use ErrorLens structured format:
{
"code": "VALIDATION_FAILED",
"message": "Validation failed",
"fieldErrors": [
{
"code": "REQUIRED_NOT_NULL",
"property": "email",
"message": "Email is required",
"rejectedValue": null,
"path": "email"
}
]
}
RFC 9457 Problem Details
Enable RFC 9457 compliant application/problem+json responses:
{
"ErrorHandling": {
"UseProblemDetailFormat": true,
"ProblemDetailTypePrefix": "https://api.example.com/errors/",
"ProblemDetailConvertToKebabCase": true
}
}
Response:
{
"type": "https://api.example.com/errors/user-not-found",
"title": "Not Found",
"status": 404,
"detail": "User abc-123 not found",
"code": "USER_NOT_FOUND"
}
Security
The library includes several security-focused features to prevent information disclosure and protect your API:
5xx Safe Message Behavior
All 5xx-class errors (500-599) automatically return a generic safe message instead of the raw exception message:
{
"code": "INTERNAL_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.
Note: 4xx errors (400-499) preserve their original messages since these are typically user-facing and safe to expose.
Message Sanitization
The BadRequestExceptionHandler automatically sanitizes Kestrel-internal error messages, replacing framework-specific details with user-safe equivalents:
{
"code": "BAD_REQUEST",
"message": "Bad request"
}
This prevents internal framework implementation details from being exposed.
Startup Validation
The JsonFieldNames configuration is validated at application startup:
- Null or empty values are rejected with clear error messages
- Duplicate field names are detected and reported
- All properties must be unique to prevent JSON serialization conflicts
This fails-fast behavior prevents misconfiguration from causing runtime errors.
Logging
Control logging verbosity per HTTP status code or exception type:
ErrorHandling:
ExceptionLogging: WithStacktrace # None | MessageOnly | WithStacktrace
LogLevels:
4xx: Warning
5xx: Error
404: Debug
FullStacktraceHttpStatuses:
- 5xx
FullStacktraceClasses:
- MyApp.CriticalException
Error Code Strategies
| Strategy | Example Input | Output |
|---|---|---|
AllCaps (default) |
UserNotFoundException |
USER_NOT_FOUND |
FullQualifiedName |
UserNotFoundException |
MyApp.Exceptions.UserNotFoundException |
Default HTTP Status Mappings
| Exception Type | HTTP Status |
|---|---|
ArgumentException / ArgumentNullException |
400 Bad Request |
InvalidOperationException |
400 Bad Request |
FormatException |
400 Bad Request |
OperationCanceledException |
499 Client Closed Request |
UnauthorizedAccessException |
401 Unauthorized |
KeyNotFoundException |
404 Not Found |
FileNotFoundException |
404 Not Found |
DirectoryNotFoundException |
404 Not Found |
TimeoutException |
408 Request Timeout |
NotImplementedException |
501 Not Implemented |
| All others | 500 Internal Server Error |
OpenTelemetry Tracing
ErrorLens automatically creates Activity spans when handling exceptions — zero configuration needed. Just wire up your OpenTelemetry collector:
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("ErrorLens.ErrorHandling") // Subscribe to ErrorLens activities
.AddConsoleExporter());
Each error-handling span includes tags: error.code, error.type, http.response.status_code, plus an exception event with OTel semantic conventions.
Error Message Localization
Opt-in localization for error messages using ASP.NET Core's IStringLocalizer:
builder.Services.AddErrorHandlingLocalization<SharedResource>();
Error codes are used as resource keys. When a translation exists, it replaces the default message. Falls back to the original message when no translation is found.
OpenAPI / Swagger Integration
Automatically add error response schemas to your API documentation:
// .NET 9+ (Microsoft.AspNetCore.OpenApi)
builder.Services.AddErrorHandlingOpenApi();
// .NET 6-8 (Swashbuckle)
builder.Services.AddErrorHandlingSwashbuckle();
Auto-generates schemas for 400, 404, and 500 responses on all endpoints. Respects [ProducesResponseType] attributes and reflects your JsonFieldNames and UseProblemDetailFormat settings.
Rate Limiting
Write structured 429 responses from ASP.NET Core's rate limiter:
builder.Services.AddRateLimiter(options =>
{
options.OnRejected = async (context, token) =>
{
var writer = context.HttpContext.RequestServices.GetRequiredService<IRateLimitResponseWriter>();
await writer.WriteRateLimitResponseAsync(context.HttpContext, context.Lease, token);
};
});
Response includes Retry-After header and structured JSON body. Configurable via ErrorHandling:RateLimiting section.
Samples
| Sample | Description |
|---|---|
MinimalApiSample |
Zero-config minimal API setup |
FullApiSample |
Controllers, custom handlers, response customizers |
ShowcaseSample |
All features: YAML config, custom field names, attributes, custom handlers, Problem Details, Swashbuckle integration |
IntegrationSample |
New v1.3.0 features: OpenTelemetry tracing, localization, OpenAPI schemas, rate limiting |
Documentation
- Getting Started
- Configuration
- Validation Errors
- Custom Handlers
- Attributes
- Response Customization
- Problem Details (RFC 9457)
- JSON Field Names
- Logging
- API Reference
- YAML Template
- Telemetry
- Localization
- OpenAPI Integration
- Swashbuckle Integration
- Rate Limiting
- Changelog
🤝 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.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
