Elarion

Result & error model

Lookup reference for Result<T>, Result, Unit, AppError, and ErrorKind, with each kind's mapping to JSON-RPC error codes and HTTP status.

A lookup reference for the result and error types. For the conceptual model and how these flow through handlers and decorators, see Results & errors.

All four core types live in Elarion.Abstractions. The two mappers live in the transport packages: Elarion.AppErrorMapper (JSON-RPC) and Elarion.AspNetCore.HttpAppErrorMapper (HTTP).

Result<T>

Elarion.Abstractions.Result<T> — a readonly struct wrapping either a success value or an AppError. It implements IResultLike and IResultFailureFactory<Result<T>>.

Result<T>
public readonly struct Result<T> : IResultLike, IResultFailureFactory<Result<T>> {
    public bool IsSuccess { get; }
    public T Value { get; }       // valid only when IsSuccess is true
    public AppError Error { get; } // valid only when IsSuccess is false

    public static Result<T> Success(T value);
    public static Result<T> Failure(AppError error);

    public static implicit operator Result<T>(T value);      // success
    public static implicit operator Result<T>(AppError error); // failure
}

The implicit conversions let a handler return a response value or an AppError directly:

return new Response(project.Id);                 // implicit Success
return AppError.NotFound($"Project {id} not found"); // implicit Failure

Result (non-generic)

Elarion.Abstractions.Result — the no-value companion for operations that produce no response, returned by the IHandler<T> convenience interface. It converts to Result<Unit> so it never enters the generic dispatch/decorator pipeline.

Result
public readonly struct Result : IResultLike, IResultFailureFactory<Result> {
    public bool IsSuccess { get; }
    public AppError Error { get; } // valid only when IsSuccess is false

    public static Result Success();
    public static Result Failure(AppError error);

    public static implicit operator Result(AppError error); // failure

    public Result<Unit> ToResultUnit();
    public static implicit operator Result<Unit>(Result result);
}

Unit

Elarion.Abstractions.Results.Unit — a readonly struct with exactly one value, used as the "no value" payload for Result<Unit>. All Unit values are equal.

Unit
public readonly struct Unit : IEquatable<Unit> {
    public static Unit Value => default; // the single value
    // == always true, != always false, ToString() => "()"
}

Unit lives in the dedicated Elarion.Abstractions.Results namespace — not the root Elarion.Abstractions namespace that handlers import for IHandler/Result/AppError. Unit is a common domain noun (units of measure, org units, rental units), so keeping it out of the always-imported namespace avoids CS0104 ambiguity with a domain Unit type. You rarely name Unit directly — prefer the IHandler<T> sugar and Result.Success(). When you do, add using Elarion.Abstractions.Results;, and if a domain Unit is also in scope, alias one of them:

using ResultUnit = Elarion.Abstractions.Results.Unit; // or: using DomainUnit = MyApp.Domain.Unit;

IResultLike

Elarion.Abstractions.IResultLike — the non-generic seam decorators use to inspect success/failure without knowing T:

IResultLike
public interface IResultLike {
    bool IsSuccess { get; }
}

Both Result<T> and Result implement it, so a decorator can branch on the outcome:

if (response is IResultLike { IsSuccess: true }) {
    await transaction.CommitAsync(ct);
} else {
    await transaction.RollbackAsync(ct);
}

AppError

Elarion.Abstractions.AppError — a transport-agnostic sealed record describing the kind of failure, not how a protocol should report it:

AppError
public sealed record AppError {
    public required ErrorKind Kind { get; init; }
    public required string Message { get; init; }
    public object? Data { get; init; }

    // Shared singleton for the generic internal-failure case.
    public static readonly AppError InternalError; // Kind = Internal, Message = "Internal error"
}

Factory methods

FactoryKindNotes
AppError.Validation(string message, object? data = null)ValidationOptional structured Data payload.
AppError.Validation(string message, IReadOnlyList<string> errors)ValidationWraps errors in ValidationErrorData as Data.
AppError.NotFound(string message)NotFound
AppError.Conflict(string message)Conflict
AppError.Forbidden(string message)Forbidden
AppError.BusinessRule(string message, object? data = null)BusinessRuleOptional structured Data payload.
AppError.Internal(string message, object? data = null)InternalOptional structured Data payload.
AppError.Validation("Invalid command.", new[] { "Name is required.", "Email is invalid." });
AppError.NotFound($"Client {id} was not found.");
AppError.BusinessRule("Credit limit exceeded.", data: limitInfo);

ValidationErrorData is the payload shape for the list-of-messages overload:

ValidationErrorData
public sealed record ValidationErrorData {
    public required IReadOnlyList<string> Errors { get; init; }
}

ErrorKind

Elarion.Abstractions.ErrorKind — the semantic category. The transport layer maps it to protocol-specific codes.

ErrorKindMeaning
ValidationInvalid input or constraint violation.
NotFoundThe requested resource does not exist.
ConflictThe operation conflicts with existing state (duplicate, concurrent modification).
ForbiddenThe caller is not authorized to perform this operation.
BusinessRuleA domain business rule was violated.
InternalAn unexpected internal error occurred.

ErrorKind mappings

Kind is the seam between application semantics and protocol codes. Each host owns its mapping; the tables below are the framework defaults. The unknown/default branch of both mappers falls through to the Internal row.

JSON-RPC error code

From Elarion.AppErrorMapper.MapToCode(ErrorKind). Protocol-level kinds reuse the JSON-RPC 2.0 spec codes; the remaining application kinds use the reserved server range (-32000 to -32099). AppErrorMapper.ToRpcError(AppError) wraps the code with the error's Message and Data.

ErrorKindJSON-RPC codeNote
Validation-32602Spec "Invalid params".
NotFound-32001Server range.
Conflict-32002Server range.
Forbidden-32003Server range.
BusinessRule-32004Server range.
Internal-32603Spec "Internal error".
(default)-32603Falls back to internal error.

HTTP status code

From Elarion.AspNetCore.HttpAppErrorMapper.MapToStatusCode(ErrorKind), used by ElarionHttpResults when translating a failed Result<T> into an RFC 7807 ProblemDetails response.

ErrorKindHTTP status
Validation400 Bad Request
NotFound404 Not Found
Conflict409 Conflict
Forbidden403 Forbidden
BusinessRule422 Unprocessable Entity
Internal500 Internal Server Error
(default)500 Internal Server Error

The JSON-RPC table is the default used by RpcDispatcherExtensions.MapHandler. Applications that need different codes register methods via the raw JsonRpcDispatcher.Map API and supply their own mapping.

On this page