ErrorApi.AspNetCore 1.0.1

dotnet add package ErrorApi.AspNetCore --version 1.0.1
                    
NuGet\Install-Package ErrorApi.AspNetCore -Version 1.0.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="ErrorApi.AspNetCore" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ErrorApi.AspNetCore" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="ErrorApi.AspNetCore" />
                    
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 ErrorApi.AspNetCore --version 1.0.1
                    
#r "nuget: ErrorApi.AspNetCore, 1.0.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 ErrorApi.AspNetCore@1.0.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=ErrorApi.AspNetCore&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=ErrorApi.AspNetCore&version=1.0.1
                    
Install as a Cake Tool

ErrorApi

A Roslyn source generator that turns a Result<T> error catalog into a Minimal API error mapper and an OpenAPI document that actually lists the failures — plus a TypeScript union for the client.

Result libraries fixed the return type. They did not fix the contract: ErrorOr, FluentResults and friends map a failure to ProblemDetails at runtime, so the OpenAPI document still says 200 OK and nothing else. The frontend has no idea a 409 Orders.AlreadyPaid exists until it happens in production.

ErrorApi resolves that at compile time — and it does so whether you use its own Result<T>, ErrorOr, OneOf, language-ext, a hand-rolled discriminated union, or no result type at all: plain exceptions work too.

What it looks like

Swagger UI, POST /orders/{id}/pay — five responses, each carrying its own codes, titles and example bodies. Note the 422: two different failures share one status, and each keeps its own code.

Swagger UI showing the pay endpoint with 200, 404, 409, 410 and 422 responses, each listing its error codes and example problem documents

Scalar API reference listing all five endpoints, each with its error responses

dotnet run --project samples/Sample.Api

http://localhost:5080/swagger · /scalar · /openapi/v1.json · /openapi/errors.ts — and the same API is built once per declaration style under samples/, each with its own README, all producing the identical contract.

First steps

One package is the whole install — it brings the generator, the primitives and the ASP.NET Core integration (and the Swashbuckle filter on .NET 8/9):

dotnet add package ErrorApi

1. Declare the catalog — the class declares membership and the default status, the members declare the names; nothing else needs typing:

[ErrorCatalog("Orders", StatusCodes.Status404NotFound)]
public static partial class OrderErrors
{
    public static partial Error NotFound { get; }                              // Orders.NotFound, 404

    [Error(StatusCodes.Status409Conflict, Detail = "Order {0} was already paid.")]
    public static partial Error AlreadyPaid(Guid orderId);                     // Orders.AlreadyPaid, 409
}

2. Return the entriesError converts implicitly into Result<T>:

public Result<Order> GetById(Guid id) =>
    _orders.TryGetValue(id, out var order) ? order : OrderErrors.NotFound;

3. Wire it up onceAddErrorApi() is the whole minimal setup, and every knob is a lambda on it:

builder.Services.AddOpenApi();
builder.Services.AddErrorApi();          // or AddErrorApi(x => x.AddExceptionHandler().Include(...))

app.MapGet("/orders/{id:guid}", (Guid id, IOrderService s) => s.GetById(id).ToHttpResult());

On .NET 8/9 the document comes through Swagger — the ErrorApi package already carries the filter, so services.AddSwaggerGen(c => c.AddErrorApiResponses()); replaces AddOpenApi() and that is all. A .NET 10 project that stays on Swagger adds ErrorApi.Swashbuckle for the identical responses, built by the same shared code.

Already on ErrorOr, OneOf, language-ext, FluentResults, Ardalis.Result or CSharpFunctionalExtensions? Take the matching adapter package and keep your types — often a bare [Error] on what you already wrote is the entire onboarding. See docs/adapters.md and docs/getting-started.md.

How it works

  1. The generator reads your [Error]/[ErrorCatalog] declarations into a catalog — codes, statuses and titles inferred from what is already written (names, bodies, base constructors).
  2. It finds every endpoint — Minimal API Map* call sites and attribute-routed controllers — and walks each handler through the call graph: into interfaces and their implementations, past mediators via the message type, into pipeline behaviours, and across assembly boundaries through baked-in exports.
  3. What it cannot see, it says out loud: thirteen EAPI diagnostics report stopped walks, drifting codes and unreachable entries at build time, instead of letting the contract lie.
  4. It emits a reflection-free model — switch statements, no runtime scan — that one OpenAPI transformer (or the Swashbuckle filter) and a TypeScript writer render from.
  5. At runtime the same model maps every failure to application/problem+json carrying a stable code member, so the response always matches the document.

The full mechanics: docs/discovery.md · docs/catalog.md · docs/typescript.md.

Why it is worth it

  • The contract stops lying. Every reachable failure is documented per endpoint, and the response body always matches — same model on both sides.
  • Nothing is written twice. Codes come from names or bodies, statuses from catalogs or base constructors; duplication is what the diagnostics hunt down, not what the library asks for.
  • Keep your result library. Seven adapters produce byte-identical contracts; exceptions work too.
  • The client gets a compiler. errors.ts turns catch folklore into an exhaustive union — add a failure server-side and the frontend build breaks instead of production.
  • Free at runtime. No reflection on the request path, native-AOT clean, and measured: success costs the same as hand-written TypedResults.Ok.

ErrorOr solves the return type, NSwag solves the client shape — neither answers "which errors does this endpoint return?". ErrorApi answers exactly that question and leans on the others for the rest.

Benchmarks

BenchmarkDotNet, raw TypedResults as the floor. One machine (x64, .NET 10):

Mean Allocated
Success path (any adapter) vs TypedResults.Ok 4.2–5.9 ns vs 4.5 ns 24 B vs 24 B
Generated lookups (FindError, type switch, route switch) 2.5–5.8 ns 0 B
Failure → application/problem+json 60–81 ns 304–328 B

And per framework, measured together on one CI runner (Ubuntu 24.04, shared hardware — read as relative, not absolute):

.NET Success (any adapter) vs raw Ok Lookups Failure → problem
10 5.5–7.3 ns / 24 B 8.4 ns / 24 B 2.6–4.4 ns / 0 B 61–76 ns
9 8.6–11.3 ns / 24 B 7.9 ns / 24 B 1.6–4.1 ns / 0 B 92–130 ns
8 9.1–12.9 ns / 48 B 7.9 ns / 48 B 1.8–4.7 ns / 0 B 65–99 ns (OneOf 202 ns)

The story holds on every framework the packages ship for: success at the floor's cost (the 48 B on net8 is the framework's own Ok<int> box, identical for hand-written code), lookups allocation-free. CI re-runs all three on every push and keeps the results as per-commit artifacts.

Full tables, methodology and the optimizations the first run bought: docs/performance.md · benchmarks/.

Documentation

docs/getting-started.md the quickstart in detail, exceptions, package-owned failure types
docs/catalog.md declaring entries, inference rules, the "which attribute, when" table
docs/adapters.md ErrorOr, OneOf, language-ext, FluentResults, Ardalis, CFE — and version compatibility
docs/discovery.md the call-graph walk, boundaries, versioned routes, diagnostics, known limits
docs/typescript.md the generated client contract
docs/performance.md benchmarks and native AOT
docs/repository.md layout, build & test, how this sits next to the alternatives
specs/ the feature specifications: requirements and their acceptance gates
AGENTS.md the map for coding agents and contributors: invariants and the checks that must pass

MIT licensed.

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (9)

Showing the top 5 NuGet packages that depend on ErrorApi.AspNetCore:

Package Downloads
ErrorApi.Swashbuckle

Swashbuckle integration for ErrorApi: an operation filter that documents every endpoint's reachable failures, byte-identical to the built-in OpenAPI transformer. The road to ErrorApi documents on net8/net9, and for any project that stays on Swagger.

ErrorApi.LanguageExt

Maps language-ext results onto Minimal API results and ErrorApi's documented error catalog.

ErrorApi.ErrorOr

Maps ErrorOr results onto Minimal API results and ErrorApi's documented error catalog.

ErrorApi.OneOf

Maps OneOf results onto Minimal API results and ErrorApi's documented error catalog.

ErrorApi.CSharpFunctionalExtensions

Maps CSharpFunctionalExtensions results onto Minimal API results and ErrorApi's documented error catalog.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 224 8/31/2026
1.0.0 190 8/30/2026