Skip to content

API Reference

Complete reference for all Pragmatic.Result types and methods.

A discriminated union representing either a success with a value or a failure with an error.

public readonly struct Result<TValue, TError> : IResultBase
where TError : IError
MethodDescription
Success(TValue value)Creates a successful result with the given value
Failure(TError error)Creates a failed result with the given error
PropertyTypeDescription
IsSuccessboolTrue if the result is successful
IsFailureboolTrue if the result is a failure
ValueTValueThe success value (throws if failure)
ErrorTErrorThe error (throws if success)
MethodDescription
TryGetValue(out TValue? value)Returns true and sets value if successful
TryGetError(out TError? error)Returns true and sets error if failed
GetValueOrDefault(TValue fallback)Returns value if success, otherwise fallback
GetValueOrDefault(Func<TError, TValue> factory)Returns value if success, or factory result
GetValueOrThrow()Returns value or throws with error details
MethodDescription
Match<T>(onSuccess, onFailure)Executes function based on state, returns result
Match(onSuccess, onFailure)Executes action based on state
MethodDescription
Map<TNew>(Func<TValue, TNew>)Transforms success value, preserves error
MapError<TNewError>(Func<TError, TNewError>)Transforms error, preserves success
Bind<TNew>(Func<TValue, Result<TNew, TError>>)Chains to another Result-returning function
MethodDescription
Ensure(predicate, errorFactory)Validates success value, converts to failure if predicate fails
MethodDescription
Tap(Action<TValue>)Executes action on success, returns same result
OnSuccess(Action<TValue>)Alias for Tap
OnFailure(Action<TError>)Executes action on failure, returns same result
MethodDescription
OrElse(Func<TError, Result<TValue, TError>>)Provides fallback Result on failure
Recover(Func<TError, TValue>)Recovers with value on failure
var (isSuccess, value, error) = result;
Result<User, NotFoundError> result = user; // From value
Result<User, NotFoundError> result = notFound; // From error

A result type for operations that succeed or fail but don’t return a value.

public readonly struct VoidResult<TError> : IResultBase
where TError : IError
MethodDescription
Success()Creates a successful void result
Failure(TError error)Creates a failed void result
PropertyTypeDescription
IsSuccessboolTrue if successful
IsFailureboolTrue if failed
ErrorTErrorThe error (throws if success)
MethodDescription
TryGetError(out TError? error)Returns true and sets error if failed
Match<T>(onSuccess, onFailure)Pattern matching
Tap(Action)Side effect on success
OnSuccess(Action)Alias for Tap
OnFailure(Action<TError>)Side effect on failure
OrElse(Func<TError, VoidResult<TError>>)Fallback on failure

Represents an optional value (Some or None). Use when absence is normal, not exceptional.

public readonly struct Maybe<T>
MethodDescription
Some(T value)Creates a Maybe with a value
None()Creates an empty Maybe
PropertyTypeDescription
HasValueboolTrue if has a value
ValueTThe value (throws if None)
MethodDescription
TryGetValue(out T? value)Safe value access
GetValueOrDefault(T fallback)Returns value or fallback
Match<TResult>(onSome, onNone)Pattern matching
Map<TNew>(Func<T, TNew>)Transform value if present
Bind<TNew>(Func<T, Maybe<TNew>>)Chain to another Maybe
ToResult<TError>(errorFactory)Convert to Result, None becomes error

Source-generated variants for operations that can fail in multiple ways.

Result<User, NotFoundError, ForbiddenError> GetUser(int id, ClaimsPrincipal user);
Result<Order, NotFoundError, ForbiddenError, ValidationError> CreateOrder(OrderRequest request);

Supports up to 8 error types. Match requires handlers for all error types:

result.Match(
user => Ok(user),
notFound => NotFound(),
forbidden => Forbid()
);

All methods support async pipelines with CancellationToken.

MethodDescription
MapAsync(mapper, ct)Transform success value
MapAsync(asyncMapper, ct)Transform with async mapper
BindAsync(binder, ct)Chain to Result-returning function
BindAsync(asyncBinder, ct)Chain to async Result-returning function
MatchAsync(onSuccess, onFailure, ct)Pattern matching
TapAsync(asyncAction, ct)Async side effect on success
OnFailureAsync(asyncAction, ct)Async side effect on failure
EnsureAsync(asyncPredicate, errorFactory, ct)Async validation
OrElseAsync(asyncFallback, ct)Async fallback on failure
MethodDescription
MapAsync(asyncMapper, ct)Start async pipeline from sync Result
BindAsync(asyncBinder, ct)Start async pipeline from sync Result
TapAsync(asyncAction, ct)Async side effect
OnFailureAsync(asyncAction, ct)Async side effect on failure
EnsureAsync(asyncPredicate, errorFactory, ct)Async validation
OrElseAsync(asyncFallback, ct)Async fallback
MethodDescription
MatchAsync(onSuccess, onFailure, ct)Pattern matching
ThenAsync(next, ct)Chain another void operation
TapAsync(asyncAction, ct)Async side effect on success
OnFailureAsync(asyncAction, ct)Async side effect on failure
OrElseAsync(asyncFallback, ct)Async fallback

Combines multiple results. Fails immediately on first error.

// Combine 2 results
var combined = Result.Combine(result1, result2); // Result<(T1, T2), TError>
// Combine 3 results
var combined = Result.Combine(result1, result2, result3); // Result<(T1, T2, T3), TError>
// Up to 5 results supported

Collects all errors instead of stopping at first.

var results = new[] { result1, result2, result3 };
var collected = results.CollectAll(); // Result<TValue[], AggregateError<TError>>
// Access all errors
if (collected.TryGetError(out var aggregate))
{
foreach (var error in aggregate.Errors)
Console.WriteLine(error.Code);
}

Built-in error types with HTTP semantics.

All HTTP errors implement IHttpError:

public interface IHttpError : IError
{
int StatusCode { get; }
string? Title { get; }
}
NotFoundError.Create("User", userId);
NotFoundError.Create("Order", orderId, "Order was deleted");
// Properties
error.EntityType // "User"
error.EntityId // "42"
error.Detail // Optional detail message
UnauthorizedError.InvalidCredentials();
UnauthorizedError.TokenExpired();
UnauthorizedError.Create("Custom reason");
ForbiddenError.Create("admin/users", "delete");
new ForbiddenError { Resource = "settings", Operation = "write" };
ConflictError.AlreadyExists("User", email);
ConflictError.ConcurrencyConflict("Order");
// Properties
error.EntityType // "User"
error.EntityId // "user@example.com"
BadRequestError.Create("Invalid date format");
BadRequestError.Create("Invalid value", "startDate");
// Properties
error.Reason // "Invalid date format"
error.ParameterName // "startDate" (optional)
InternalServerError.Create("Database connection failed");
InternalServerError.FromException(ex);
// Implements IDiagnosticError for exception details
error.ExceptionType // "SqlException"
error.ExceptionMessage // "Connection timeout"

// Basic registration
builder.Services.AddPragmaticResult();
// With custom message resolver
builder.Services.AddPragmaticResult<LocalizedErrorResolver>();
// Apply to endpoint group
app.MapGroup("api").WithResultHandling();
// Skip for specific endpoint
app.MapGet("/raw", [SkipResultHandling] () => GetResult());
// Global registration
builder.Services.AddControllers(opt => opt.Filters.Add<ResultActionFilter>());
// Skip for specific action
[SkipResultHandling]
public async Task<Result<User, NotFoundError>> GetRaw(int id) => ...;
// IResult (Minimal API)
result.ToHttpResult(factory); // 200 or ProblemDetails
result.ToHttpResult(factory, 201); // Custom success status
result.ToCreatedResult(factory, "/users/42"); // 201 with Location
result.ToNoContentResult(factory); // 204 or ProblemDetails
// IActionResult (Controllers)
result.ToActionResult(factory);
result.ToActionResult(factory, 201);
result.ToCreatedAtActionResult(factory, "Get", new { id });

// For Minimal APIs
builder.Services.ConfigureHttpJsonOptions(opt =>
opt.SerializerOptions.Converters.Add(new ResultJsonConverterFactory()));
// For Controllers
builder.Services.AddControllers()
.AddJsonOptions(opt =>
opt.JsonSerializerOptions.Converters.Add(new ResultJsonConverterFactory()));
// Success
{
"isSuccess": true,
"value": { "id": 42, "name": "John" }
}
// Failure
{
"isSuccess": false,
"error": { "code": "NOT_FOUND", "entityType": "User", "entityId": "42" }
}

Automatically generates correct schemas for Result types.

builder.Services.AddOpenApi(opt =>
{
opt.AddSchemaTransformer<ResultSchemaTransformer>();
});

Transforms Result<User, NotFoundError> endpoints to show:

  • 200 OK with User schema
  • 404 Not Found with ProblemDetails schema

Base interface for all error types.

public interface IError
{
string Code { get; } // Semantic code like "NOT_FOUND"
}

Extends IError with HTTP semantics.

public interface IHttpError : IError
{
int StatusCode { get; }
string? Title { get; }
}

For errors with diagnostic information (exceptions).

public interface IDiagnosticError
{
string? ExceptionType { get; }
string? ExceptionMessage { get; }
string? StackTrace { get; }
}

Marker interface for Result type identification at runtime.

public interface IResultBase
{
bool IsSuccess { get; }
bool IsFailure { get; }
}

For custom error message resolution (localization).

public interface IErrorMessageResolver
{
string? GetMessage(IError error);
}

Container for multiple errors from CollectAll operations.

public readonly struct AggregateError<TError> : IError
where TError : IError
{
public string Code => "AGGREGATE_ERROR";
public IReadOnlyList<TError> Errors { get; }
public int Count { get; }
}