App-vNext/Polly · error · TimeoutRejectedException
The delegate executed through TimeoutPolicy did not complete
Error message
The delegate executed through TimeoutPolicy did not complete within the timeout.
What it means
Runtime TimeoutRejectedException thrown by the legacy TimeoutEngine when the executed delegate did not finish before the timeout elapsed. The engine observes an OperationCanceledException together with the timeout CancellationTokenSource being in a cancellation-requested state, then calls the onTimeout callback and throws TimeoutRejectedException carrying the applied timeout and the original exception. Unlike the 10 argument-validation errors above, this fires during policy execution, not construction.
Source
Thrown at src/Polly/Timeout/TimeoutEngine.cs:65
*/
actionTask.Wait(timeoutCancellationTokenSource.Token);
}
catch (AggregateException ex) when (ex.InnerExceptions.Count == 1)
{
// Issue #270. Unwrap extra AggregateException caused by the way pessimistic timeout policy for synchronous executions is necessarily constructed.
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
return actionTask.Result;
}
catch (Exception ex)
{
// Note that we cannot rely on testing (operationCanceledException.CancellationToken == combinedToken || operationCanceledException.CancellationToken == timeoutCancellationTokenSource.Token)
// as either of those tokens could have been onward combined with another token by executed code, and so may not be the token expressed on operationCanceledException.CancellationToken.
if (ex is OperationCanceledException && timeoutCancellationTokenSource.IsCancellationRequested)
{
onTimeout(context, timeout, actionTask, ex);
throw new TimeoutRejectedException("The delegate executed through TimeoutPolicy did not complete within the timeout.", timeout, ex);
}
throw;
}
}
}
View on GitHub (pinned to d0e46bdb1e)
Solutions
- Increase the timeout to match the realistic latency of the wrapped delegate, or fix the delegate to be faster.
- Combine Timeout with Retry (using a PolicyWrap) so transient timeouts are retried with backoff.
- Ensure the delegate honors the CancellationToken — Optimistic timeout relies on cooperative cancellation; pass ct to HttpClient/EF/etc.
- For non-cooperative code, use TimeoutStrategy.Pessimistic so the policy abandons the task instead of waiting on cancellation.
- Add a Fallback policy in the wrap to return a default or cached value when TimeoutRejectedException fires.
Example fix
// before var policy = Policy.TimeoutAsync(TimeSpan.FromSeconds(1)); await policy.ExecuteAsync(async ct => await httpClient.GetAsync(url, ct), CancellationToken.None); // throws on slow network // after var policy = Policy.TimeoutAsync(TimeSpan.FromSeconds(5)); var retry = Policy.Handle<TimeoutRejectedException>().WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(i)); var wrap = Policy.WrapAsync(retry, policy); await wrap.ExecuteAsync(async ct => await httpClient.GetAsync(url, ct), CancellationToken.None);
Defensive patterns
Strategy: try-catch
Validate before calling
if (timeout <= TimeSpan.Zero) throw new InvalidOperationException("timeout must be positive");
// runtime: instrument the delegate's own latency budget so the timeout reflects p99 Type guard
static bool IsTimeoutRejected(Exception e) => e is TimeoutRejectedException;
Try / catch
try
{
await policy.ExecuteAsync(async ct => await work(ct), cancellationToken);
}
catch (TimeoutRejectedException ex)
{
logger.LogWarning(ex, "delegate exceeded timeout {Timeout}", ex.Timeout);
// degrade gracefully, return cached/default, or rethrow to an outer fallback
} Prevention
- Always pass the CancellationToken into downstream calls so Optimistic timeout can cancel cooperatively.
- Tune the timeout from real latency metrics (p99), not a guess.
- Wrap Timeout inside Retry and/or Fallback policies to absorb transient slowness.
- Use Pessimistic strategy for delegates that cannot honor cancellation.
- Set a command timeout on the underlying client (HttpClient, SqlCommand) as a secondary guard.
When it happens
Trigger: The wrapped delegate runs slower than the configured timeout under Optimistic strategy (cancellation cooperatively observed) — or, under Pessimistic strategy, the delegate did not observe cancellation and was abandoned. Slow downstream calls (HTTP, DB), deadlocks, or unbounded loops all trigger it.
Common situations: An HTTP call to a degraded upstream that takes longer than the policy's timeout; a database query without its own command timeout; optimistic cancellation not honored by a blocking synchronous call; timeout set lower than the real p99 latency of the dependency; cascading latency under load.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- The delegate executed asynchronously through TimeoutPolicy d
- onTimeoutAsync
- seconds
- timeout
- timeoutProvider
AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13).
Data as JSON: /api/errors/d200254090efed8a.
Report an issue: GitHub.