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

The synchronous sibling of the async case: calling Execute<TResult> on a non-generic FallbackPolicy throws InvalidOperationException because a void fallback action cannot produce a substitute TResult. Build a generic FallbackPolicy<TResult> via Policy<TResult>.Handle<...>().Fallback<TResult>(...) when you need to return a fallback value.

Source

Thrown at src/Polly/Fallback/FallbackPolicy.cs:44

            (ctx, token) =>
            {
                action(ctx, token);
                return EmptyStruct.Instance;
            },
            context,
            ExceptionPredicates,
            ResultPredicates<EmptyStruct>.None,
            (outcome, ctx) => _onFallback(outcome.Exception, ctx),
            (outcome, ctx, ct) =>
            {
                _fallbackAction(outcome.Exception, ctx, ct);
                return EmptyStruct.Instance;
            },
            cancellationToken);

    /// <inheritdoc/>
    protected override TResult Implementation<TResult>(Func<Context, CancellationToken, TResult> action, Context context, CancellationToken cancellationToken) =>
        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 returning a value of type <typeparamref name="TResult"/>.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
public class FallbackPolicy<TResult> : Policy<TResult>, IFallbackPolicy<TResult>
{
    private readonly Action<DelegateResult<TResult>, Context> _onFallback;
    private readonly Func<DelegateResult<TResult>, Context, CancellationToken, TResult> _fallbackAction;

    internal FallbackPolicy(
        PolicyBuilder<TResult> policyBuilder,
        Action<DelegateResult<TResult>, Context> onFallback,
        Func<DelegateResult<TResult>, Context, CancellationToken, TResult> fallbackAction)

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Use Policy<TResult>.Handle<...>().Fallback<TResult>(fallbackValue/fallbackAction, onFallback) and call Execute (non-generic TResult is inferred).
  2. Keep non-generic FallbackPolicy paired with non-generic Execute only.
  3. Align the declared policy variable's generic-arity with the Execute<TResult> you intend to call.

Example fix

// before
var policy = Policy
    .Handle<InvalidOperationException>()
    .Fallback((ex, ctx, ct) => { /* void */ });
int v = policy.Execute<int>(() => Compute()); // throws

// after
var policy = Policy<int>
    .Handle<InvalidOperationException>()
    .Fallback(fallbackValue: 0, onFallback: outcome => { });
int v = policy.Execute(() => Compute());
Defensive patterns

Strategy: type-guard

Validate before calling

var policy = Policy<int>.Handle<InvalidOperationException>()
    .Fallback(fallbackValue: 0, onFallback: _ => { });
int v = policy.Execute(() => Compute());

Type guard

static bool IsResultFallbackPolicy(object p) => p is IFallbackPolicy<int>;

Try / catch

try { return policy.Execute<TResult>(action); } catch (InvalidOperationException ex) when (ex.Message.Contains("non-generic FallbackPolicy")) { /* fix arity */ throw; }

Prevention

When it happens

Trigger: Policy.Handle<X>().Fallback(...) (non-generic, sync) followed by policy.Execute<TResult>(...).

Common situations: Migrating a void synchronous fallback to a value-returning one without switching to Policy<TResult>; using a shared policy field declared as non-generic Policy for a result-returning call site.

Related errors


AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13). Data as JSON: /api/errors/5b705d7ab53d06e4. Report an issue: GitHub.