Skip to content

CallResult<T> ​

CallResult<T> is a readonly struct returned by the TryRunAsync overloads. It carries the outcome of a resilience operation and the attempt history.

MemberDescription
IsSuccesstrue if an attempt returned a value that the classifier identified as Ok. This is the test a fallback branches on.
ValueThe value returned by the final attempt, or default if every attempt threw an exception. This is populated even on failure - for example, a final 503 Service Unavailable response is returned so the caller can dispose of it.
ReturnedValuetrue if an attempt got as far as returning something, so Value holds what it returned. Not the same question as IsSuccess: when the last attempt returned a value the classifier refused, this is true and IsSuccess is false. Branch on IsSuccess to decide whether to serve the value; branch on this one to decide whether there is something to dispose.
ExceptionThe exception thrown by the last attempt, or a library-specific exception (such as a deadline timeout).
ReasonThe StopReason the execution loop stopped for.
AttemptsThe log of all attempts made during the call.
TryGetValue(out T value)true if the call succeeded. This is the recommended method for most call sites to check for success.
ValueOrThrow()Returns the value if the call succeeded, otherwise rethrows the failure exception with its original stack trace intact.
ThrowIfFailed()Rethrows the failure exception, with its original stack trace intact, if there was one. Use it when you want the exception but not the value.

CallResult (the non-generic version) provides the same members without the four about a value: Value, ReturnedValue, TryGetValue, and ValueOrThrow.

Note: TryRunAsync still throws an exception if the caller's CancellationToken is cancelled.

CallResult<IAsyncEnumerable<T>> - what the streaming TryRunAsync returns - is the one shape with a rule of its own. Its Value is an enumeration the policy has already started, so ReturnedValue is never true on a failure, and a successful value is enumerable once and implements IAsyncDisposable: enumerate it, or dispose it when you decide not to.

Example: implement a fallback ​

Use the result to serve a fallback value when a call fails:

csharp
private async Task<User> ReadUserAsync(UserCache cache, CancellationToken cancellationToken)
{
    var result = await Resilience.Http.TryRunAsync(attempt => FetchAsync(cancellationToken: attempt), cancellationToken: cancellationToken);

    if (result.TryGetValue(value: out var user))
        return user;

    _logger.LogWarning(message: "Serving the cached user: {Reason} after {Attempts}", result.Reason, result.Attempts);
    return cache.LastKnownGood;
}

StopReason ​

The StopReason enum, carried on Reason, says why the resilience loop stopped.

ValueMeaning
SucceededAn attempt returned a result that the classifier identified as Ok.
PermanentThe outcome was classified as Permanent, so the handler did not retry.
AttemptsExhaustedThe maximum number of attempts allowed by the policy was reached.
DeadlineExceededThe overall wall-clock budget for the call expired.
BudgetExhaustedThe retry budget refused to fund another attempt.
DependencyUnavailableA circuit breaker refused to execute the call.
DrainingThe process is shutting down, so the call stopped with the failure it had rather than starting another attempt. The failure is the dependency's own, not a CallRejectedException - see Drain-aware shutdown.

AttemptLog ​

AttemptLog is a sealed class that implements IReadOnlyList<Attempt>.

MemberDescription
CountThe number of attempts executed.
ElapsedThe wall-clock time from the start of the call until the final attempt returned.
this[int index]The attempt at the specified 0-based index.
AttemptLog.EmptyA static instance of an empty log.
AttemptLog.Of(Exception)Extracts the log attached to an exception that the library rethrew.
AttemptLog.DataKeyThe Exception.Data key used to store the log: "NResilience.Attempts".
ToString()Returns a human-readable summary of the attempts and delays.

TryRunAsync always materializes the log; RunAsync materializes it only when a call is about to fail.

Attempt ​

Attempt is a readonly struct representing a single completed attempt.

MemberDescription
NumberThe 1-based index of the attempt.
DurationThe time taken for the callback to execute.
DelayBeforeThe backoff delay served immediately before this attempt (zero for the first attempt).
VerdictThe classification of the outcome. The kind is recorded; RetryAfter is not, because it is observable as the next attempt's DelayBefore.
ExceptionThe exception thrown by this attempt, or null if it returned a value.
RemainingThe time remaining on the deadline when the attempt started.
StartOffsetWhen the attempt started, measured from the start of the call. Two entries whose ranges overlap ran at the same time.
IsHedgedWhether this attempt was started as a copy of one that had not come back yet.
IsDiscardedWhether this attempt was cancelled because a sibling answered first. Such an attempt was never classified, so its Verdict carries no information.

Released under the MIT License.