Skip to main content
The generator maps ErrorType enum values to HTTP status codes and BCL TypedResults types following RFC 9110 semantics.

ErrorType → HTTP Mapping

ErrorTypeHTTPTitleTypedResults FactoryReturn Type
Validation400Bad RequestValidationProblem(errors)ValidationProblem
Unauthorized401UnauthorizedUnauthorized()UnauthorizedHttpResult
Forbidden403ForbiddenForbid()ForbidHttpResult
NotFound404Not FoundNotFound(problem)NotFound<ProblemDetails>
Conflict409ConflictConflict(problem)Conflict<ProblemDetails>
Failure500Internal Server ErrorInternalServerError(problem)InternalServerError<ProblemDetails>
Unexpected500Internal Server ErrorInternalServerError(problem)InternalServerError<ProblemDetails>
Failure and Unexpected both map to 500, not 422. These are server errors per RFC 9110. For 422, use Error.Custom(422, ...).

Success Type → HTTP Mapping

ErrorOr TypeHTTPTypedResultsUse Case
ErrorOr<T> (value)200Ok<T>GET, PUT returning entity
ErrorOr<Success>200OkGeneric success
ErrorOr<Created>201CreatedPOST without body
ErrorOr<T> + [Post]201Created<T>POST returning entity
ErrorOr<Updated>200OkPUT/PATCH success
ErrorOr<Deleted>204NoContentDELETE success

Custom Error Codes

Use Error.Custom() for status codes not covered by ErrorType:
// Direct mapping to specific TypedResult
Error.Custom(400, "Code", "Description")  // BadRequest<ProblemDetails>
Error.Custom(422, "Code", "Description")  // UnprocessableEntity<ProblemDetails>
Error.Custom(429, "Code", "Description")  // ProblemHttpResult (429)
Error.Custom(503, "Code", "Description")  // ProblemHttpResult (503)

Custom Error Resolution

NumericTypeTypedResults Factory
400BadRequest(problem)
401Unauthorized()
403Forbid()
404NotFound(problem)
409Conflict(problem)
422UnprocessableEntity(problem)
500InternalServerError(problem)
Other 4xx/5xxProblem(statusCode: code)

ProblemDetails (RFC 7807)

All error responses use RFC 7807 ProblemDetails format:
{
  "type": "https://httpstatuses.io/404",
  "title": "Not Found",
  "status": 404,
  "detail": "Todo with ID 123 was not found",
  "code": "Todo.NotFound",
  "traceId": "00-abc123..."
}

Validation Errors

Multiple validation errors aggregate into HttpValidationProblemDetails:
// Multiple validation errors
return new[]
{
    Error.Validation("Name.Required", "Name is required"),
    Error.Validation("Email.Invalid", "Email format is invalid")
};
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Name.Required": ["Name is required"],
    "Email.Invalid": ["Email format is invalid"]
  }
}

Union Type Generation

The generator creates Results<...> union types for OpenAPI documentation:
// Handler
[Get("/todos/{id}")]
public static ErrorOr<Todo> GetById(int id) { ... }

// Generated union (inferred from possible error types)
Results<Ok<Todo>, NotFound<ProblemDetails>, InternalServerError<ProblemDetails>>

BCL Limit

The BCL provides Results<T1, T2, ..., T6> - maximum 6 type parameters. When more types are needed:
  1. IResult fallback - Loses compile-time safety
  2. Consolidate errors - Use ProblemHttpResult for multiple codes
  3. Custom metadata - Implement IEndpointMetadataProvider

Type Deduplication

Failure and Unexpected both map to InternalServerError<ProblemDetails>, so only one appears in the union.

Type Ordering

Types are ordered by status code (ascending):
Results<
    Ok<Todo>,                           // 200
    Created<Todo>,                       // 201
    NoContent,                           // 204
    ValidationProblem,                   // 400
    NotFound<ProblemDetails>,            // 404
    Conflict<ProblemDetails>,            // 409
    InternalServerError<ProblemDetails>  // 500
>

Security Considerations

401 Unauthorized and 403 Forbidden return no body to prevent information leakage:
// Correct: No body
ErrorType.Unauthorized => TypedResults.Unauthorized()
ErrorType.Forbidden => TypedResults.Forbid()