Skip to main content
ErrorOrX provides discriminated union types for .NET with a fluent API for error handling and automatic ASP.NET Core Minimal API integration.

Packages

PackagePurpose
ErrorOrXRuntime library with ErrorOr<T> type
ErrorOrX.GeneratorsSource generator for Minimal API endpoints

Installation

dotnet add package ErrorOrX.Generators
ErrorOrX.Generators automatically includes ErrorOrX as a dependency.

Quick Start

using ErrorOr;

// Define an endpoint handler
public static class TodoEndpoints
{
    [Get("/todos/{id}")]
    public static ErrorOr<Todo> GetById(int id)
    {
        var todo = _db.Find(id);
        if (todo is null)
            return Error.NotFound("Todo.NotFound", $"Todo {id} not found");
        return todo;
    }
}

// Register in Program.cs
app.MapErrorOrEndpoints();
The generator produces:
  • MapErrorOrEndpoints() extension method
  • Typed Results<...> union for OpenAPI
  • Automatic parameter binding
  • JSON serialization context (AOT-compatible)

Core Types

ErrorOr<T>

A discriminated union that holds either a value of type T or one or more Error instances.
ErrorOr<User> result = GetUser(id);

// Check state
if (result.IsError)
{
    // Handle errors
    foreach (var error in result.Errors)
        Console.WriteLine($"{error.Code}: {error.Description}");
}
else
{
    // Use value
    var user = result.Value;
}

Fluent API

MethodPurpose
Then()Chain operations on success
Else()Provide fallback on error
Match()Transform to result
Switch()Execute side effects
FailIf()Conditional failure
var result = GetUser(id)
    .Then(user => GetOrders(user.Id))
    .Then(orders => orders.Where(o => o.Status == "Active"))
    .Else(errors => Array.Empty<Order>());

Or Extensions

Convert nullable values to ErrorOr<T>:
// Returns Error.NotFound if null
User? user = FindUser(id);
ErrorOr<User> result = user.OrNotFound("User.NotFound", "User not found");

// Other extensions
value.OrValidation("Field.Invalid", "Invalid field");
value.OrUnauthorized("Auth.Failed", "Authentication failed");
value.OrForbidden("Access.Denied", "Access denied");
value.OrConflict("Resource.Exists", "Resource already exists");
value.OrFailure("Operation.Failed", "Operation failed");
value.OrError(customError);

Success Sentinels

For endpoints that don’t return a value:
[Post("/todos")]
public static ErrorOr<Created> Create(CreateTodoRequest req)
{
    _db.Add(new Todo(req.Title));
    return Result.Created;
}

[Delete("/todos/{id}")]
public static ErrorOr<Deleted> Delete(int id)
{
    _db.Remove(id);
    return Result.Deleted;
}

[Put("/todos/{id}")]
public static ErrorOr<Updated> Update(int id, UpdateTodoRequest req)
{
    _db.Update(id, req);
    return Result.Updated;
}
SentinelHTTP StatusTypedResult
Result.Success200Ok
Result.Created201Created
Result.Updated200Ok
Result.Deleted204NoContent