Skip to content

CallResult<T>

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

MemberDescription
IsSuccesstrue if an attempt returned a value that the classifier identified as Ok.
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.
HasValuetrue if Value contains a result actually returned by an attempt.
ExceptionThe exception thrown by the last attempt, or a library-specific exception (such as a deadline timeout).
StopReasonThe reason the execution loop stopped.
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.

CallResult (the non-generic version) provides the same members without Value, HasValue, or TryGetValue, and adds the ThrowIfFailed() method.

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

Example: Implement a fallback

You can use the result to provide a fallback value when a resilience operation 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.StopReason, result.Attempts);
    return cache.LastKnownGood;
}

StopReason

The StopReason enum indicates why the resilience loop stopped executing.

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.

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.

While TryRunAsync always materializes the log, RunAsync only materializes it 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.

Released under the MIT License.