Ave.Extensions.ErrorPaths 0.0.2-preview.1

This is a prerelease version of Ave.Extensions.ErrorPaths.
dotnet add package Ave.Extensions.ErrorPaths --version 0.0.2-preview.1
                    
NuGet\Install-Package Ave.Extensions.ErrorPaths -Version 0.0.2-preview.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Ave.Extensions.ErrorPaths" Version="0.0.2-preview.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ave.Extensions.ErrorPaths" Version="0.0.2-preview.1" />
                    
Directory.Packages.props
<PackageReference Include="Ave.Extensions.ErrorPaths" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Ave.Extensions.ErrorPaths --version 0.0.2-preview.1
                    
#r "nuget: Ave.Extensions.ErrorPaths, 0.0.2-preview.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Ave.Extensions.ErrorPaths@0.0.2-preview.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Ave.Extensions.ErrorPaths&version=0.0.2-preview.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Ave.Extensions.ErrorPaths&version=0.0.2-preview.1&prerelease
                    
Install as a Cake Tool

Ave.Extensions.ErrorPaths

A NuGet library providing hierarchical, path-based error codes as value types for .NET.

Why This Library?

The Problem

When building applications with multiple layers or libraries, error handling becomes fragmented:

  • String-based errors are flexible but impossible to match programmatically. Is "User not found" the same error as "user_not_found" or "UserNotFound"?
  • Enum-based errors are type-safe but don't compose across library boundaries. Your UserService.Error.NotFound can't be matched against OrderService.Error.NotFound without manual mapping.
  • Exception hierarchies require try-catch blocks and don't work well with Result types for explicit error handling.

The Solution

Error codes as hierarchical paths that work like file paths:

Validation.Required.Email
NotFound.Entity.User
IO.Database.Connection

This gives you:

  • Hierarchical matching: Catch all validation errors with Validation._, or be specific with Validation.Required.Email
  • Cross-library compatibility: Any library can define Validation.Required.CustomField and it automatically matches Validation._
  • No enum mapping: Libraries don't need to know about each other's error types
  • Composability: Extend existing codes with the / operator: ErrorCodes.Validation.Required / "Email"

Key Advantages

Approach Type-Safe Composable Cross-Library Hierarchical
Strings No Yes Yes No
Enums Yes No No No
Exceptions Yes Yes Partial Yes
ErrorPaths Yes Yes Yes Yes

Real-World Example

// In your domain layer
Result<User, Error> GetUser(int id) =>
    Result<User, Error>.Failure(Errors.NotFound("User", id));

// In your API layer - match ANY not-found error, regardless of source
if (result.HasError(ErrorCodes.NotFound._))
    return Results.NotFound();

// Or be specific
if (result.HasError(ErrorCodes.NotFound.Entity))
    return Results.NotFound();

Your API layer doesn't need to know whether the error came from UserService, OrderService, or a third-party library. If it's a NotFound error, it matches.

Packages

Package Description Target
Ave.Extensions.ErrorPaths Core library with ErrorCode, Error, and factory methods netstandard2.0
Ave.Extensions.ErrorPaths.Functional Integration with Ave.Extensions.Functional Result types netstandard2.0
Ave.Extensions.ErrorPaths.Serialization JSON serialization with System.Text.Json netstandard2.0
Ave.Extensions.ErrorPaths.AspNetCore ASP.NET Core integration (ProblemDetails, HTTP status) net10.0
Ave.Extensions.ErrorPaths.FluentAssertions FluentAssertions extensions for Error and ErrorCode types netstandard2.0

Installation

dotnet add package Ave.Extensions.ErrorPaths

Quick Start

Creating Errors

using Ave.Extensions.ErrorPaths;

// Use well-known error codes
var error = new Error(ErrorCodes.Validation.Required, "Email is required.");

// Use factory methods
var notFound = Errors.NotFound("User", 42);
var validation = Errors.Required("email");
var timeout = Errors.Timeout("database query");

// Create custom error codes with the / operator
var customCode = ErrorCodes.Validation.Required / "Email" / "Format";
var customError = new Error(customCode, "Invalid email format.");

Error Code Hierarchy

Error codes form a hierarchy using dot notation. You can match errors against ancestor codes:

var error = new Error(ErrorCodes.Validation.Required, "Field required.");

// Check if error matches a category
error.Is(ErrorCodes.Validation._);        // true
error.Is(ErrorCodes.Validation.Required); // true
error.Is(ErrorCodes.NotFound._);          // false

Adding Metadata

var error = Errors.Required("email")
    .With("maxLength", 100)
    .With("allowedDomains", new[] { "example.com" });

// Access metadata
var field = error.Metadata["field"]; // "email"

Error Chaining

var innerError = Errors.Network("Connection refused.");
var outerError = innerError.Wrap(ErrorCodes.IO.Database, "Failed to connect to database.");

// Access the chain
outerError.Inner.Value.Message; // "Connection refused."

Integration with Ave.Extensions.Functional

using Ave.Extensions.ErrorPaths;
using Ave.Extensions.ErrorPaths.Functional;
using Ave.Extensions.Functional;

Result<User, Error> GetUser(int id)
{
    // Return errors using the Error type
    return Result<User, Error>.Failure(Errors.NotFound("User", id));
}

// Check for specific error types
var result = GetUser(42);
if (result.HasError(ErrorCodes.NotFound._))
{
    // Handle not found
}

// Transform errors
var wrapped = result.WrapError(ErrorCodes.Internal._, "User lookup failed.");

// Map errors
var mapped = result.MapError(e => e.With("timestamp", DateTime.UtcNow));

JSON Serialization

using Ave.Extensions.ErrorPaths;
using Ave.Extensions.ErrorPaths.Serialization;
using System.Text.Json;

var options = ErrorPathsJsonOptions.Configure();
// or with defaults (camelCase, indented)
var options = ErrorPathsJsonOptions.CreateDefault();

var error = Errors.Required("email");
var json = JsonSerializer.Serialize(error, options);
// {"code":"Validation.Required","message":"The field 'email' is required.","metadata":{"field":"email"}}

var deserialized = JsonSerializer.Deserialize<Error>(json, options);

ASP.NET Core Integration

using Ave.Extensions.ErrorPaths;
using Ave.Extensions.ErrorPaths.AspNetCore;

app.MapGet("/users/{id}", (int id) =>
{
    var result = GetUser(id);
    if (result.IsFailure)
    {
        return result.Error.ToHttpResult();
    }
    return Results.Ok(result.Value);
});

HTTP Status Code Mapping

Default mappings:

Error Code HTTP Status
Validation.* 400 Bad Request
NotFound.* 404 Not Found
Auth.Unauthorized 401 Unauthorized
Auth.Forbidden 403 Forbidden
Auth.TokenExpired 401 Unauthorized
IO.Timeout 504 Gateway Timeout
IO.* 502 Bad Gateway
Internal.* 500 Internal Server Error

Custom mappings:

ErrorCodeHttpMapping.Register(ErrorCodes.Validation._, HttpStatusCode.UnprocessableEntity);

ProblemDetails

var error = Errors.NotFound("User", 42);
var problemDetails = error.ToProblemDetails();

// Result:
// {
//   "type": "urn:error:NotFound.Entity",
//   "title": "Not Found",
//   "status": 404,
//   "detail": "User with id '42' was not found.",
//   "entity": "User",
//   "id": 42
// }

FluentAssertions Extensions

using Ave.Extensions.ErrorPaths;
using Ave.Extensions.ErrorPaths.FluentAssertions;

var error = Errors.Required("email");

// Assert on error code and message
error.Should().HaveCode(ErrorCodes.Validation.Required);
error.Should().HaveMessage("The field 'email' is required.");

// Match against ancestor codes
error.Should().MatchCode(ErrorCodes.Validation._);
error.Should().NotMatchCode(ErrorCodes.NotFound._);

// Assert on metadata
error.Should().HaveMetadata();
error.Should().HaveMetadataEntry("field", "email");

// Assert on inner errors with chaining
var outer = innerError.Wrap(ErrorCodes.Internal.Unexpected, "Operation failed.");
outer.Should().HaveInnerError()
    .Which.Should().HaveCode(ErrorCodes.IO.Network);

// Assert on ErrorCode properties
var code = new ErrorCode("Validation.Required");
code.Should().HaveValue("Validation.Required");
code.Should().BeChildOf(new ErrorCode("Validation"));
code.Should().HaveDepth(2);
code.Should().HaveLeaf("Required");
code.Should().HaveParent(new ErrorCode("Validation"));

Well-Known Error Codes

ErrorCodes.Validation._           // Validation
ErrorCodes.Validation.Required    // Validation.Required
ErrorCodes.Validation.Format      // Validation.Format
ErrorCodes.Validation.Range       // Validation.Range
ErrorCodes.Validation.Length      // Validation.Length
ErrorCodes.Validation.Pattern     // Validation.Pattern
ErrorCodes.Validation.Duplicate   // Validation.Duplicate
ErrorCodes.Validation.Invalid     // Validation.Invalid

ErrorCodes.NotFound._             // NotFound
ErrorCodes.NotFound.Entity        // NotFound.Entity
ErrorCodes.NotFound.File          // NotFound.File
ErrorCodes.NotFound.Resource      // NotFound.Resource

ErrorCodes.Auth._                 // Auth
ErrorCodes.Auth.Unauthorized      // Auth.Unauthorized
ErrorCodes.Auth.Forbidden         // Auth.Forbidden
ErrorCodes.Auth.TokenExpired      // Auth.TokenExpired
ErrorCodes.Auth.TokenInvalid      // Auth.TokenInvalid

ErrorCodes.IO._                   // IO
ErrorCodes.IO.Network             // IO.Network
ErrorCodes.IO.Timeout             // IO.Timeout
ErrorCodes.IO.FileSystem          // IO.FileSystem
ErrorCodes.IO.Database            // IO.Database
ErrorCodes.IO.ExternalService     // IO.ExternalService

ErrorCodes.Internal._             // Internal
ErrorCodes.Internal.Unexpected    // Internal.Unexpected
ErrorCodes.Internal.Configuration // Internal.Configuration
ErrorCodes.Internal.Assertion     // Internal.Assertion

Factory Methods

Errors.Validation(message)           // Validation error
Errors.Required(field)               // Required field error with metadata
Errors.Format(field, expected)       // Format error with metadata
Errors.NotFound(message)             // Generic not found
Errors.NotFound(entity, id)          // Entity not found with metadata
Errors.Unauthorized(reason?)         // Authentication error
Errors.Forbidden(reason?)            // Authorization error
Errors.Timeout(operation)            // Timeout error with metadata
Errors.Network(message)              // Network error
Errors.Unexpected(message)           // Unexpected error
Errors.Unexpected(exception)         // Unexpected error from exception
Errors.From(code, message)           // Custom error code

License

MIT License - see LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  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 was computed.  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 was computed.  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 was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on Ave.Extensions.ErrorPaths:

Package Downloads
Ave.Extensions.FileSystem.Abstractions

Abstractions for file system operations across platforms.

Ave.Extensions.ErrorPaths.AspNetCore

ASP.NET Core integration for Ave.Extensions.ErrorPaths - ProblemDetails and HTTP status mapping

Ave.Extensions.ErrorPaths.FluentAssertions

FluentAssertions extensions for ErrorPaths error types

Ave.Extensions.ErrorPaths.Functional

Integration between Ave.Extensions.ErrorPaths and Ave.Extensions.Functional Result types

Ave.Extensions.ErrorPaths.Serialization

JSON serialization support for Ave.Extensions.ErrorPaths using System.Text.Json

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.2-preview.1 28 2/10/2026
0.0.1-preview.1 27 2/10/2026