Clawfoot.ResultPattern 4.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Clawfoot.ResultPattern --version 4.0.0
                    
NuGet\Install-Package Clawfoot.ResultPattern -Version 4.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Clawfoot.ResultPattern" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Clawfoot.ResultPattern" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Clawfoot.ResultPattern" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Clawfoot.ResultPattern --version 4.0.0
                    
#r "nuget: Clawfoot.ResultPattern, 4.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Clawfoot.ResultPattern@4.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Clawfoot.ResultPattern&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Clawfoot.ResultPattern&version=4.0.0
                    
Install as a Cake Tool

Clawfoot.ResultPattern

A Results pattern library for .NET: return success/failure and propagate errors across layers without throwing. Use Result and Result<T> instead of exceptions for expected failure paths.

Results are immutable. Every operation returns a new instance; chain with Invoke, WithError, WithValue, or combine with Result.Combine(...).

Null-safe. A successful Result<T> always carries a non-null value, and the library is fully annotated for nullable reference types: after a HasErrors/Success check the compiler knows Value is not null. No ! needed.

Target: .NET Standard 2.1. Upgrading from 3.x? See CHANGELOG.md.

Install

dotnet add package Clawfoot.ResultPattern

Quick usage

using Clawfoot.ResultPattern;

// Success (no value): a plain Result
Result result = Result.Ok();
if (!result) return result;

// Success with value: return the value, it converts implicitly
Result<User> FindUser(Guid id)
{
    User? user = _db.Find(id);
    if (user is null) return Result.Error("Not found", userMessage: "We couldn't find that user.");
    return user;
}

// Consuming: check, then use Value. The compiler knows it's not null.
Result<User> userResult = FindUser(id);
if (userResult.HasErrors)
    return userResult;              // or (Result)userResult to propagate into another Result<TOther>

Console.WriteLine(userResult.Value.Name);   // no '!'

Results and values

You have Use
Success, no value Result (Result.Ok())
Success with a value Result<T>: return the value, or Result.Ok(value)
Success where the value may legitimately be absent ("found nothing") (Result, T?) tuple, or a plain Result plus an out/nullable value
Failure Result.Error(...), which converts to any Result<T>

The rules, enforced by annotations, analyzers and at runtime:

  • A successful Result<T> always has a non-null Value. Creating one with null throws ArgumentNullException, and the compiler warns first ([DisallowNull]).
  • A failed result converts freely to any Result<T>, which is how errors propagate up the stack.
  • A successful plain Result cannot become a Result<T>, because there is no value to put in it. return Result.Ok(); in a Result<T> method is a compile error (CFRESULT003) and throws InvalidOperationException at runtime.

Reading the value

if (result.HasErrors) return result;   // or: if (!result.Success) / !result.IsOk / !result.HasResult
use(result.Value);

if (result.TryGetValue(out var value)) use(value);

// Tests and startup code: throws InvalidOperationException listing the errors
User user = CreateUser().GetValueOrThrow();

Assert.True(result.Success) and Assert.False(result.HasErrors) (xUnit) also tell the compiler the value is present. Fluent assertion chains like result.HasErrors.Should().BeFalse() don't, so use GetValueOrThrow() there.

Immutable patterns

Assign the returned result to keep chaining or combining:

// Add an error → new result (a Result<T> keeps its value)
result = result.WithError("Validation failed");

// Combine multiple results → new result (errors combined; for Result<T>, last successful value wins)
Result combined = Result.Combine(result1, result2);
Result<int> combinedT = Result.Combine(r1, r2, r3);
Result<User> withValidation = Result.Combine(validationResult, userResult);

// Chain Invoke (returns new result on exception)
result = result.Invoke(() => ValidateInput(input)).Invoke(() => SaveToDb(entity));

// InvokeResult returns Result<T>
Result<int> r = start.InvokeResult(() => GetCount());

Error handling in a flow

Use Invoke / InvokeAsync to run code and capture exceptions into a new result (no mutation):

var result = Result.Ok()
    .Invoke(() => ValidateInput(input))
    .Invoke(() => SaveToDb(entity));

if (!result) return result;

// Async
var result = await Result.Ok()
    .InvokeAsync(async () => await FetchAndSaveAsync());

Enum-based errors

Decorate an enum with [Error] and create results from it:

public enum UserErrors
{
    [Error(Code = 404, Message = "User not found", UserMessage = "We couldn't find that user.")]
    NotFound,
    [Error(Code = 400, Message = "Invalid id")]
    InvalidId
}

Result r = Result.FromError(UserErrors.NotFound);

Analyzers

The package includes Roslyn analyzers. They load automatically, with no extra package to install.

ID Default Flags
CFRESULT001 Warning Discarded result of With*, Invoke, InvokeAsync, Combine (results are immutable, so the call does nothing)
CFRESULT002 Warning result.Value!. Check HasErrors/Success instead, or use GetValueOrThrow() in tests
CFRESULT003 Error A known-successful Result (Result.Ok(), new Result()) converted to Result<T> (implicitly, by cast, or with As<T>())
CFRESULT004 Warning Result<X?>: a nullable type argument can't represent "no value"; use (Result, X?)

Raise or lower severities in .editorconfig:

[*.cs]
dotnet_diagnostic.CFRESULT002.severity = error
dotnet_diagnostic.CFRESULT004.severity = error

Features

  • Result and Result<T>: immutable; Success, IsOk, HasErrors, HasResult, Errors, Message, Value, TryGetValue, GetValueOrThrow
  • Nullable flow: HasErrors/Success/IsOk/HasResult/TryGetValue tell the compiler when Value is non-null
  • Result.Combine(...): combines multiple results (errors combined; for Result<T>, last successful value wins)
  • WithError / WithErrors / WithException / WithValue: return a new result with added state
  • Invoke / InvokeAsync / InvokeResult: wrap calls and return a new result (chainable)
  • Error and IError: structured errors (Code, Message, UserMessage, GroupName, MemberName)
  • ErrorAttribute and Error.From(enum) for enum-driven error messages
  • Implicit conversion of Result to bool (if (!result) ...)

License

LGPL-3.0-only. © 2026 Douglas Gaskell / Clawfoot Software

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.2.0 140 10/4/2026
4.1.0 98 10/4/2026
4.0.0 105 10/3/2026
3.2.1 158 2/24/2026
3.2.0 794 2/16/2026
3.1.0 138 2/16/2026
3.0.1 134 2/16/2026
3.0.0 137 2/15/2026