Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
E

error-or

> 编程语言
Open source

A simple, fluent discriminated union of an error or a result.

2.1K stars0 likes0 views
WebsiteGitHub

About

A simple, fluent discriminated union of an error or a result.

  • Give it a star ⭐!
  • Getting Started
    • Replace throwing exceptions with ErrorOr<T>
    • Support For Multiple Errors
    • Various Functional Methods and Extension Methods
      • Real world example
      • Simple Example with intermediate steps
        • No Failure
        • Failure
  • Creating an ErrorOr instance
    • Using implicit conversion
    • Using The ErrorOrFactory
    • Using The ToErrorOr Extension Method
  • Properties
    • IsError
    • IsSuccess
    • Value
    • Errors
    • FirstError
    • ErrorsOrEmptyList
  • Methods
    • Match
      • Match
      • MatchAsync
      • MatchFirst
      • MatchFirstAsync
    • Switch
      • Switch
      • SwitchAsync
      • SwitchFirst
      • SwitchFirstAsync
    • Then
      • Then
      • ThenAsync
      • ThenDo and ThenDoAsync
      • ThenEnsure and ThenEnsureAsync
      • Mixing Then, ThenDo, ThenAsync, ThenDoAsync
    • FailIf
    • Else
      • Else
      • ElseAsync
      • ElseDo and ElseDoAsync
  • Mixing Features (Then, FailIf, Else, Switch, Match)
  • Recording Outcomes
  • Error Types
    • Built in error types
    • Custom error types
  • Built in result types (Result.Success, ..)
  • Organizing Errors
  • Mediator + FluentValidation + ErrorOr
  • Contribution
  • Credits
  • License

Give it a star ⭐

Loving it? Show your support by giving this project a star!

Getting Started

Replace throwing exceptions with ErrorOr<T>

This

cs
public float Divide(int a, int b)
{
    if (b == 0)
    {
        throw new Exception("Cannot divide by zero");
    }

    return a / b;
}

try
{
    var result = Divide(4, 2);
    Console.WriteLine(result * 2); // 4
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
    return;
}

Turns into this

cs
public ErrorOr<float> Divide(int a, int b)
{
    if (b == 0)
    {
        return Error.Unexpected(description: "Cannot divide by zero");
    }

    return a / b;
}

var result = Divide(4, 2);

if (result.IsError)
{
    Console.WriteLine(result.FirstError.Description);
    return;
}

Console.WriteLine(result.Value * 2); // 4

Or, using Then/Else and Switch/Match, you can do this

cs

Divide(4, 2)
    .Then(val => val * 2)
    .SwitchFirst(
        onValue: Console.WriteLine, // 4
        onFirstError: error => Console.WriteLine(error.Description));

Support For Multiple Errors

Internally, the ErrorOr object has a list of Errors, so if you have multiple errors, you don't need to compromise and have only the first one.

…

Various Functional Methods and Extension Methods

The ErrorOr object has a variety of methods that allow you to work with it in a functional way.

This allows you to chain methods together, and handle the result in a clean and concise way.

Real world example

cs
return await _userRepository.GetByIdAsync(id)
    .Then(user => user.IncrementAge()
        .Then(success => user)
        .Else(errors => Error.Unexpected("Not expected to fail")))
    .FailIf(user => !user.IsOverAge(18), UserErrors.UnderAge)
    .ThenDo(user => _logger.LogInformation($"User {user.Id} incremented age to {user.Age}"))
    .ThenAsync(user => _userRepository.UpdateAsync(user))
    .Match(
        _ => NoContent(),
        errors => errors.ToActionResult());

Simple Example with intermediate steps

No Failure

…

Failure

…

Creating an ErrorOr instance

Using implicit conversion

There are implicit converters from TResult, Error, List<Error> to ErrorOr<TResult>

cs
ErrorOr<int> result = 5;
ErrorOr<int> result = Error.Unexpected();
ErrorOr<int> result = [Error.Validation(), Error.Validation()];
cs
public ErrorOr<int> IntToErrorOr()
{
    return 5;
}
cs
public ErrorOr<int> SingleErrorToErrorOr()
{
    return Error.Unexpected();
}
cs
public ErrorOr<int> MultipleErrorsToErrorOr()
{
    return [
        Error.Validation(description: "Invalid Name"),
        Error.Validation(description: "Invalid Last Name")
    ];
}

Using The ErrorOrFactory

cs
ErrorOr<int> result = ErrorOrFactory.From(5);
ErrorOr<int> result = ErrorOrFactory.From<int>(Error.Unexpected());
ErrorOr<int> result = ErrorOrFactory.From<int>([Error.Validation(), Error.Validation()]);
cs
ErrorOr<int> result = await ErrorOrFactory.FromAsync(5);
ErrorOr<int> result = await ErrorOrFactory.FromAsync<int>(Error.Unexpected());
ErrorOr<int> result = await ErrorOrFactory.FromAsync<int>([Error.Validation(), Error.Validation()]);
cs
public ErrorOr<int> GetValue()
{
    return ErrorOrFactory.From(5);
}
cs
public ErrorOr<int> SingleErrorToErrorOr()
{
    return ErrorOrFactory.From<int>(Error.Unexpected());
}
cs
public ErrorOr<int> MultipleErrorsToErrorOr()
{
    return ErrorOrFactory.From([
        Error.Validation(description: "Invalid Name"),
        Error.Validation(description: "Invalid Last Name")
    ]);
}
cs
public async Tak<ErrorOr<int>> GetValueAsync()
{
    return await ErrorOrFactory.FromAsync(5);
}
cs
public async Tak<ErrorOr<int>> SingleErrorToErrorOrAsync()
{
    return await ErrorOrFactory.FromAsync<int>(Error.Unexpected());
}
cs
public async Tak<ErrorOr<int>> MultipleErrorsToErrorOrAsync()
{
    return await ErrorOrFactory.FromAsync([
        Error.Validation(description: "Invalid Name"),
        Error.Validation(description: "Invalid Last Name")
    ]);
}

Using The ToErrorOr Extension Method

Values

cs
ErrorOr<int> result = 5.ToErrorOr();
ErrorOr<int> result = await Task.FromResult(5).ToErrorOrAsync();

Errors

cs
ErrorOr<int> result = Error.Unexpected().ToErrorOr<int>();
ErrorOr<int> result = new[] { Error.Unauthorized(), Error.Validation() }.ToErrorOr<int>();
cs
Task<Error> errorTask = Task.FromResult(Error.Validation());
ErrorOr<int> result = errorTask.ToErrorOrAsync<int>();
cs
List<Error> errors = [Error.Unauthorized(), Error.Validation()];
Task<List<Error>> errorsTask = Task.FromResult(errors);
ErrorOr<int> result = await errorsTask.ToErrorOrAsync<int>();

Properties

IsError

cs
ErrorOr<int> result = User.Create();

if (result.IsError)
{
    // the result contains one or more errors
}

IsSuccess

cs
ErrorOr<int> result = User.Create();

if (result.IsSuccess)
{
    // the result contains no errors
}

Value

cs
ErrorOr<int> result = User.Create();

if (!result.IsError) // the result contains a value
{
    Console.WriteLine(result.Value);
}

Errors

cs
ErrorOr<int> result = User.Create();

if (result.IsError)
{
    result.Errors // contains the list of errors that occurred
        .ForEach(error => Console.WriteLine(error.Description));
}

FirstError

cs
ErrorOr<int> result = User.Create();

if (result.IsError)
{
    var firstError = result.FirstError; // only the first error that occurred
    Console.WriteLine(firstError == result.Errors[0]); // true
}

ErrorsOrEmptyList

cs
ErrorOr<int> result = User.Create();

if (result.IsError)
{
    result.ErrorsOrEmptyList // List<Error> { /* one or more errors */  }
    return;
}

result.ErrorsOrEmptyList // List<Error> { }

Methods

Match

The Match method receives two functions, onValue and onError, onValue will be invoked if the result is success, and onError is invoked if the result is an error.

Match

cs
string foo = result.Match(
    value => value,
    errors => $"{errors.Count} errors occurred.");

MatchAsync

cs
string foo = await result.MatchAsync(
    value => Task.FromResult(value),
    errors => Task.FromResult($"{errors.Count} errors occurred."));

MatchFirst

The MatchFirst method receives two functions, onValue and onError, onValue will be invoked if the result is success, and onError is invoked if the result is an error.

Unlike Match, if the state is error, MatchFirst's onError function receives only the first error that occurred, not the entire list of errors.

cs
string foo = result.MatchFirst(
    value => value,
    firstError => firstError.Description);

MatchFirstAsync

cs
string foo = await result.MatchFirstAsync(
    value => Task.FromResult(value),
    firstError => Task.FromResult(firstError.Description));

Switch

The Switch method receives two actions, onValue and onError, onValue will be invoked if the result is success, and onError is invoked if the result is an error.

Switch

cs
result.Switch(
    value => Console.WriteLine(value),
    errors => Console.WriteLine($"{errors.Count} errors occurred."));

SwitchAsync

cs
await result.SwitchAsync(
    value => { Console.WriteLine(value); return Task.CompletedTask; },
    errors => { Console.WriteLine($"{errors.Count} errors occurred."); return Task.CompletedTask; });

SwitchFirst

The SwitchFirst method receives two actions, onValue and onError, onValue will be invoked if the result is success, and onError is invoked if the result is an error.

Unlike Switch, if the state is error, SwitchFirst's onError function receives only the first error that occurred, not the entire list of errors.

cs
result.SwitchFirst(
    value => Console.WriteLine(value),
    firstError => Console.WriteLine(firstError.Description));

SwitchFirstAsync

cs
await result.SwitchFirstAsync(
    value => { Console.WriteLine(value); return Task.CompletedTask; },
    firstError => { Console.WriteLine(firstError.Description); return Task.CompletedTask; });

Then

Then

Then receives a function, and invokes it only if the result is not an error.

cs
ErrorOr<int> foo = result
    .Then(val => val * 2);

Multiple Then methods can be chained together.

cs
ErrorOr<string> foo = result
    .Then(val => val * 2)
    .Then(val => $"The result is {val}");

If any of the methods return an error, the chain will break and the errors will be returned.

cs
ErrorOr<int> Foo() => Error.Unexpected();

ErrorOr<string> foo = result
    .Then(val => val * 2)
    .Then(_ => GetAnError())
    .Then(val => $"The result is {val}") // this function will not be invoked
    .Then(val => $"The result is {val}"); // this function will not be invoked

ThenAsync

ThenAsync receives an asynchronous function, and invokes it only if the result is not an error.

cs
ErrorOr<string> foo = await result
    .ThenAsync(val => DoSomethingAsync(val))
    .ThenAsync(val => DoSomethingElseAsync($"The result is {val}"));

ThenDo and ThenDoAsync

ThenDo and ThenDoAsync

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C#

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言