App-vNext/Polly · error · InvalidOperationException
You have executed the generic .Execute<TResult> method on a
Error message
You have executed the generic .Execute<TResult> method on a non-generic FallbackPolicy. A non-generic FallbackPolicy only defines a fallback action which returns void; it can never return a substitute TResult value. To use FallbackPolicy to provide fallback TResult values you must define a generic fallback policy FallbackPolicy<TResult>. For example, define the policy as Policy<TResult>.Handle<Whatever>.Fallback<TResult>(/* some TResult value or Func<..., TResult> */);
What it means
Thrown when code calls the generic Execute<TResult>/ExecuteAsync<TResult> path on a NON-generic AsyncFallbackPolicy. A non-generic FallbackPolicy (Policy.Handle<...>.Fallback(...)) only defines a void fallback action; it cannot synthesize a substitute TResult, so the framework rejects the call with InvalidOperationException at execution time. To return fallback values you must build a generic FallbackPolicy<TResult> via Policy<TResult>.Handle<...>.Fallback<TResult>(...).
Source
Thrown at src/Polly/Fallback/AsyncFallbackPolicy.cs:49
await action(ctx, ct).ConfigureAwait(continueOnCapturedContext);
return EmptyStruct.Instance;
},
context,
ExceptionPredicates,
ResultPredicates<EmptyStruct>.None,
(outcome, ctx) => _onFallbackAsync(outcome.Exception, ctx),
async (outcome, ctx, ct) =>
{
await _fallbackAction(outcome.Exception, ctx, ct).ConfigureAwait(continueOnCapturedContext);
return EmptyStruct.Instance;
},
continueOnCapturedContext,
cancellationToken);
/// <inheritdoc/>
protected override Task<TResult> ImplementationAsync<TResult>(Func<Context, CancellationToken, Task<TResult>> action, Context context, CancellationToken cancellationToken,
bool continueOnCapturedContext) =>
throw new InvalidOperationException($"You have executed the generic .Execute<{nameof(TResult)}> method on a non-generic {nameof(FallbackPolicy)}. " +
$"A non-generic {nameof(FallbackPolicy)} only defines a fallback action which returns void; it can never return a substitute {nameof(TResult)} value. " +
$"To use {nameof(FallbackPolicy)} to provide fallback {nameof(TResult)} values you must define a generic fallback policy {nameof(FallbackPolicy)}<{nameof(TResult)}>. " +
$"For example, define the policy as Policy<{nameof(TResult)}>.Handle<Whatever>.Fallback<{nameof(TResult)}>(/* some {nameof(TResult)} value or Func<..., {nameof(TResult)}> */);");
}
/// <summary>
/// A fallback policy that can be applied to delegates.
/// </summary>
/// <typeparam name="TResult">The return type of delegates which may be executed through the policy.</typeparam>
public class AsyncFallbackPolicy<TResult> : AsyncPolicy<TResult>, IFallbackPolicy<TResult>
{
private readonly Func<DelegateResult<TResult>, Context, Task> _onFallbackAsync;
private readonly Func<DelegateResult<TResult>, Context, CancellationToken, Task<TResult>> _fallbackAction;
internal AsyncFallbackPolicy(
PolicyBuilder<TResult> policyBuilder,
Func<DelegateResult<TResult>, Context, Task> onFallbackAsync,
Func<DelegateResult<TResult>, Context, CancellationToken, Task<TResult>> fallbackAction)View on GitHub (pinned to d0e46bdb1e)
Solutions
- Define the policy generically: Policy<TResult>.Handle<Whatever>().Fallback<TResult>(fallbackValue or fallbackAction, onFallback).
- Match the Execute call's TResult to the policy's TResult; use a non-generic Execute only with a non-generic FallbackPolicy.
- If you truly want a void fallback, keep using non-generic Execute (not Execute<TResult>).
Example fix
// before
var policy = Policy
.Handle<HttpRequestException>()
.FallbackAsync(async (ex, ctx, ct) => { /* void fallback */ });
var value = await policy.ExecuteAsync<int>(ct => GetValueAsync(ct), ct); // throws
// after
var policy = Policy<int>
.Handle<HttpRequestException>()
.FallbackAsync(fallbackValue: 0, onFallbackAsync: (outcome, ctx) => Task.CompletedTask);
var value = await policy.ExecuteAsync(ct => GetValueAsync(ct), ct); Defensive patterns
Strategy: type-guard
Validate before calling
// choose the policy arity at compile time to match the result
var policy = Policy<int>.Handle<HttpRequestException>()
.FallbackAsync(fallbackValue: 0, onFallbackAsync: (o, ctx) => Task.CompletedTask);
var v = await policy.ExecuteAsync(ct => GetAsync(ct), ct); Type guard
// structural check before executing static bool IsResultFallbackPolicy(object p) => p is IFallbackPolicy<int>;
Try / catch
try { return await policy.ExecuteAsync<int>(action, ct); } catch (InvalidOperationException ex) when (ex.Message.Contains("non-generic FallbackPolicy")) { log.PolicyArityMismatch(ex); throw; } Prevention
- Always declare the policy variable with the matching generic arity (Policy<TResult>, not Policy).
- Build fallback-value policies with Policy<TResult>.Handle<...>().Fallback<TResult>(...).
- Keep a unit test that executes the policy through ExecuteAsync<TResult> to catch arity mismatches early.
When it happens
Trigger: Building the policy as Policy.Handle<X>().Fallback(...) (non-generic) and then calling ExecuteAsync<TResult>(...) or Execute<TResult>(...) on it, expecting it to yield a typed fallback value.
Common situations: Refactoring a void delegate policy into one that returns results but forgetting to switch Policy to Policy<TResult> and to the generic Fallback overload; copy-paste from a void tutorial into a value-returning handler.
Related errors
- You have executed the generic .Execute<TResult> method on a
- Value cannot be null. (Parameter 'fallbackAction')
- Value must be greater than zero.
- Value cannot be null. (Parameter 'onFallback')
- Value cannot be null. (Parameter 'action')
AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13).
Data as JSON: /api/errors/70a9ffcb498d2d12.
Report an issue: GitHub.