Skip to content

EN Error and result lifecycle

Vinícius Campos edited this page Aug 22, 2026 · 2 revisions

Error and result lifecycle

Português · English home

An Error is immutable data: Code selects a message-template entry, ErrorCode is the stable client-facing screen identifier, Kind controls transport semantics, Arguments fill template tokens, and Field identifies an input when applicable. Factory methods cover the closed ErrorKind set; Error.Custom supplies a business-specific catalog code while reusing one of those kinds.

flowchart LR
    Rule[Domain rule] --> Error[Error factory or Error.Custom]
    Error --> Failure[Result.Failure or Result of T.Failure]
    Failure --> Compose[Bind / Map / Combine / Match]
    Compose --> Boundary{Application boundary}
    Boundary -->|Minimal API| Http[ToHttpResult]
    Boundary -->|MVC| Mvc[ToActionResult]
    Boundary -->|FastEndpoints| Send[SendOffsideAsync]
    Http --> Problem[OffsideProblem]
    Mvc --> Problem
    Send --> Problem
Loading

Result represents success without a value; Result<T> carries a value on success. A failure always contains at least one error. Constructors snapshot error sequences, and Combine concatenates every failed result's errors in input order. Bind short-circuits a failure, Map transforms only success, and Match makes both branches explicit. There is deliberately no implicit conversion from T and no applicative Apply.

HTTP selection

At an ASP.NET boundary, success maps to 204 for Result and 200 with the value for Result<T>. A failure reports every error but selects one primary error by severity; ties keep the first result-order error. Kinds map to the fixed status set 400, 401, 403, 404, 409, 410, 412, 422, 429 and 500.

The response is application/problem+json with Problem Details fields plus errorCode, traceId, errors, and optionally debug. Message text is resolved only at this boundary.

Unexpected failures are explicit

The 500 sanitization path applies whenever the selected primary error has Kind == ErrorKind.Unexpected, including an Error.Custom(..., ErrorKind.Unexpected, ...); it is not limited to errors created by Error.Unexpected(...). Every unexpected error in that response gets the generic unexpected message resolved in the selected culture and a public errorCode forced to UNEXPECTED. The primary error's diagnostic detail appears in debug only when it supplied that argument and ExposeExceptionDetails is enabled. The ASP.NET adapter also attempts to log the first unexpected error when the selected status is 500.

There is no global exception handler in Offside. An arbitrary thrown exception is not converted, sanitized, logged or returned by this library. Applications must add their own exception policy. Likewise, calling error.ToException() creates a DomainException but does not install anything that catches it.

See the full factories and combinators in Domain guide, and the wire contract in ASP.NET Core.

Clone this wiki locally