RESTCLIENT.NET / DOCUMENTATION
HttpError Type
Represents HTTP-specific errors that can occur during web requests. This sealed hierarchy ensures complete error handling coverage for HTTP operations.
Namespace: Outcome
Overview
HttpError is a discriminated union with exactly two cases:
ErrorResponseError- The server returned an error response (4xx, 5xx)ExceptionError- An exception occurred (network, timeout, etc.)
public abstract partial record HttpError<TError>
Type Parameter
| Parameter | Description |
|---|---|
TError |
The type used to deserialize error response bodies |
Nested Types
ErrorResponseError
Represents an HTTP error response from the server.
public sealed record ErrorResponseError(
TError Body,
HttpStatusCode StatusCode,
HttpResponseHeaders Headers
) : HttpError<TError>
Properties
| Property | Type | Description |
|---|---|---|
Body |
TError |
The deserialized error response body |
StatusCode |
HttpStatusCode |
The HTTP status code (400, 404, 500, etc.) |
Headers |
HttpResponseHeaders |
The response headers |
ExceptionError
Represents an error caused by an exception during the HTTP operation.
public sealed record ExceptionError(Exception Exception) : HttpError<TError>
Properties
| Property | Type | Description |
|---|---|---|
Exception |
Exception |
The exception that was caught |
Properties
IsExceptionError
Returns true if this error was caused by an exception.
public bool IsExceptionError { get; }
IsErrorResponse
Returns true if this error was caused by an HTTP error response.
public bool IsErrorResponse { get; }
Static Factory Methods
FromException
Creates an HttpError from an exception.
public static HttpError<TError> FromException(Exception exception)
Example
try
{
// HTTP operation
}
catch (HttpRequestException ex)
{
return Result<User, HttpError<ApiError>>.Failure(
HttpError<ApiError>.FromException(ex)
);
}
FromErrorResponse
Creates an HttpError from a server error response.
public static HttpError<TError> FromErrorResponse(
TError body,
HttpStatusCode statusCode,
HttpResponseHeaders headers
)
Example
if (!response.IsSuccessStatusCode)
{
var errorBody = await DeserializeError(response);
return Result<User, HttpError<ApiError>>.Failure(
HttpError<ApiError>.FromErrorResponse(
errorBody,
response.StatusCode,
response.Headers
)
);
}
Instance Methods
Match
Transforms this HttpError using the provided functions.
public TError Match(
Func<Exception, TError> onException,
Func<TError, HttpStatusCode, HttpResponseHeaders, TError> onErrorResponse
)
Example
string errorMessage = httpError.Match(
onException: ex => $"Network error: {ex.Message}",
onErrorResponse: (body, status, headers) =>
$"API error {(int)status}: {body.Message}"
);
IsErrorResponseError
Attempts to extract ErrorResponseError details using out parameters.
public bool IsErrorResponseError(
out TError body,
out HttpStatusCode statusCode,
out HttpResponseHeaders? headers
)
Example
if (httpError.IsErrorResponseError(out var body, out var status, out var headers))
{
Console.WriteLine($"Status: {status}, Message: {body.Message}");
}
Pattern Matching
The recommended way to handle HttpError:
var message = httpError switch
{
HttpError<ApiError>.ErrorResponseError(var body, var status, var headers)
=> status switch
{
HttpStatusCode.NotFound => "Resource not found",
HttpStatusCode.Unauthorized => "Please log in",
HttpStatusCode.Forbidden => "Access denied",
HttpStatusCode.BadRequest => $"Invalid request: {body.Message}",
_ => $"Server error ({(int)status}): {body.Message}"
},
HttpError<ApiError>.ExceptionError(var ex)
=> ex switch
{
HttpRequestException => "Network error - check your connection",
TaskCanceledException => "Request timed out",
_ => $"Unexpected error: {ex.Message}"
}
};
With Type Aliases
global using ResponseError = Outcome.HttpError<ApiError>.ErrorResponseError;
global using ExceptionError = Outcome.HttpError<ApiError>.ExceptionError;
var message = httpError switch
{
ResponseError(var body, var status, _) => $"Error {status}: {body.Message}",
ExceptionError(var ex) => $"Exception: {ex.Message}",
};
Common Error Types
Define a strongly-typed error model:
public record ApiError(
string Message,
string? Code,
IDictionary<string, string[]>? ValidationErrors
);
Handling Validation Errors
var result = await httpClient.PostAsync(...);
if (result is Result<User, HttpError<ApiError>>.Error<User, HttpError<ApiError>>(
HttpError<ApiError>.ErrorResponseError(var error, HttpStatusCode.BadRequest, _)))
{
if (error.ValidationErrors is not null)
{
foreach (var (field, messages) in error.ValidationErrors)
{
Console.WriteLine($"{field}: {string.Join(", ", messages)}");
}
}
}
Integration with Result
HttpError is typically used as the failure type in Result:
Result<TSuccess, HttpError<TError>>
Complete Pattern Matching
var output = result switch
{
Result<User, HttpError<ApiError>>.Ok<User, HttpError<ApiError>>(var user)
=> $"Success: {user.Name}",
Result<User, HttpError<ApiError>>.Error<User, HttpError<ApiError>>(
HttpError<ApiError>.ErrorResponseError(var body, var status, _))
=> $"API Error {status}: {body.Message}",
Result<User, HttpError<ApiError>>.Error<User, HttpError<ApiError>>(
HttpError<ApiError>.ExceptionError(var ex))
=> $"Exception: {ex.Message}",
};
Best Practices
- Define error models - Create typed classes for your API's error responses
- Handle both cases - Always handle both
ErrorResponseErrorandExceptionError - Check status codes - Different status codes often require different handling
- Log exceptions -
ExceptionErrorcontains the full exception for debugging - Use type aliases - Simplify pattern matching with global using statements
- Inspect headers - Rate limit info and other metadata are in response headers