App-vNext/Polly · error · ArgumentNullException

onTimeoutAsync

Error message

onTimeoutAsync

What it means

Thrown by the legacy Polly TimeoutAsync overload that accepts a 3-argument onTimeoutAsync callback (Context, TimeSpan, Task). The method guards its onTimeoutAsync parameter with ArgumentNullException at the very top, before delegating to the 4-argument overload. This is a construction-time validation, not a runtime timeout error — the policy is never created when the timeout callback is null.

Source

Thrown at src/Polly/Timeout/AsyncTimeoutSyntax.cs:360

    /// <exception cref="ArgumentNullException">Thrown when <paramref name="onTimeoutAsync"/> is <see langword="null"/>.</exception>
    public static AsyncTimeoutPolicy TimeoutAsync(Func<Context, TimeSpan> timeoutProvider, Func<Context, TimeSpan, Task, Exception, Task> onTimeoutAsync) =>
        TimeoutAsync(timeoutProvider, TimeoutStrategy.Optimistic, onTimeoutAsync);

    /// <summary>
    /// Builds an <see cref="AsyncPolicy" /> that will wait asynchronously for a delegate to complete for a specified period of time. A <see cref="TimeoutRejectedException" /> will be thrown if the delegate does not complete within the configured timeout.
    /// </summary>
    /// <param name="timeoutProvider">A function to provide the timeout for this execution.</param>
    /// <param name="timeoutStrategy">The timeout strategy.</param>
    /// <param name="onTimeoutAsync">An action to call on timeout, passing the execution context, the timeout applied, and a <see cref="Task" /> capturing the abandoned, timed-out action.
    /// <remarks>The Task parameter will be null if the executed action responded cooperatively to cancellation before the policy timed it out.</remarks></param>
    /// <returns>The policy instance.</returns>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="timeoutProvider"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="onTimeoutAsync"/> is <see langword="null"/>.</exception>
    public static AsyncTimeoutPolicy TimeoutAsync(Func<Context, TimeSpan> timeoutProvider, TimeoutStrategy timeoutStrategy, Func<Context, TimeSpan, Task, Task> onTimeoutAsync)
    {
        if (onTimeoutAsync == null)
        {
            throw new ArgumentNullException(nameof(onTimeoutAsync));
        }

        return TimeoutAsync(timeoutProvider, timeoutStrategy, (ctx, timeout, task, _) => onTimeoutAsync(ctx, timeout, task));
    }

    /// <summary>
    /// Builds an <see cref="AsyncPolicy" /> that will wait asynchronously for a delegate to complete for a specified period of time. A <see cref="TimeoutRejectedException" /> will be thrown if the delegate does not complete within the configured timeout.
    /// </summary>
    /// <param name="timeoutProvider">A function to provide the timeout for this execution.</param>
    /// <param name="timeoutStrategy">The timeout strategy.</param>
    /// <param name="onTimeoutAsync">An action to call on timeout, passing the execution context, the timeout applied, the <see cref="Task" /> capturing the abandoned, timed-out action, and the captured <see cref="Exception"/>.
    /// <remarks>The Task parameter will be null if the executed action responded cooperatively to cancellation before the policy timed it out.</remarks></param>
    /// <returns>The policy instance.</returns>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="timeoutProvider"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="onTimeoutAsync"/> is <see langword="null"/>.</exception>
    public static AsyncTimeoutPolicy TimeoutAsync(
        Func<Context, TimeSpan> timeoutProvider,
        TimeoutStrategy timeoutStrategy,

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Pass a non-null Func<Context, TimeSpan, Task, Task>, e.g. an inline lambda (_, __, ___) => Task.CompletedTask if you have no work to do on timeout.
  2. If you do not need a timeout callback at all, call the 2-argument overload TimeoutAsync(timeoutProvider, timeoutStrategy) which wires an empty default handler.
  3. Trace where the null value originates (DI registration, optional parameter, uninitialized field) and fix the assignment at the source.

Example fix

// before
Policy.TimeoutAsync(provider, TimeoutStrategy.Optimistic, null);

// after
Policy.TimeoutAsync(provider, TimeoutStrategy.Optimistic, (ctx, ts, task) => Task.CompletedTask);
Defensive patterns

Strategy: validation

Validate before calling

if (onTimeoutAsync is null) throw new ArgumentException("onTimeoutAsync must be supplied", nameof(onTimeoutAsync));
var policy = Policy.TimeoutAsync(provider, TimeoutStrategy.Optimistic, onTimeoutAsync);

Type guard

static bool HasTimeoutHandler(Func<Context, TimeSpan, Task, Task> cb) => cb is not null;

Prevention

When it happens

Trigger: Calling Policy.TimeoutAsync(timeoutProvider, timeoutStrategy, null) where the third argument (the Func<Context, TimeSpan, Task, Task> onTimeoutAsync) is null. Also triggered when a variable holding the callback was never assigned or was conditionally set to null.

Common situations: Refactoring code where the onTimeout callback was previously inlined and later extracted into a nullable field; DI containers injecting an unset callback; copy-paste from a simpler overload that omits the callback.

Understand the failure class

Related errors


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