{"record":{"id":"d200254090efed8a","repo":"App-vNext/Polly","slug":"the-delegate-executed-through-timeoutpolicy-did-no","errorCode":null,"errorMessage":"The delegate executed through TimeoutPolicy did not complete within the timeout.","messagePattern":"The delegate executed through TimeoutPolicy did not complete within the timeout\\.","errorType":"exception","errorClass":"TimeoutRejectedException","httpStatus":null,"severity":"error","filePath":"src/Polly/Timeout/TimeoutEngine.cs","lineNumber":65,"sourceCode":"                 */\n                actionTask.Wait(timeoutCancellationTokenSource.Token);\n            }\n            catch (AggregateException ex) when (ex.InnerExceptions.Count == 1)\n            {\n                // Issue #270. Unwrap extra AggregateException caused by the way pessimistic timeout policy for synchronous executions is necessarily constructed.\n                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();\n            }\n\n            return actionTask.Result;\n        }\n        catch (Exception ex)\n        {\n            // Note that we cannot rely on testing (operationCanceledException.CancellationToken == combinedToken || operationCanceledException.CancellationToken == timeoutCancellationTokenSource.Token)\n            // 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.\n            if (ex is OperationCanceledException && timeoutCancellationTokenSource.IsCancellationRequested)\n            {\n                onTimeout(context, timeout, actionTask, ex);\n                throw new TimeoutRejectedException(\"The delegate executed through TimeoutPolicy did not complete within the timeout.\", timeout, ex);\n            }\n\n            throw;\n        }\n    }\n}\n","sourceCodeStart":47,"sourceCodeEnd":72,"githubUrl":"https://github.com/App-vNext/Polly/blob/d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac/src/Polly/Timeout/TimeoutEngine.cs#L47-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar policy = Policy.TimeoutAsync(TimeSpan.FromSeconds(1));\nawait policy.ExecuteAsync(async ct => await httpClient.GetAsync(url, ct), CancellationToken.None); // throws on slow network\n\n// after\nvar policy = Policy.TimeoutAsync(TimeSpan.FromSeconds(5));\nvar retry = Policy.Handle<TimeoutRejectedException>().WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(i));\nvar wrap = Policy.WrapAsync(retry, policy);\nawait wrap.ExecuteAsync(async ct => await httpClient.GetAsync(url, ct), CancellationToken.None);","handlingStrategy":"try-catch","validationCode":"if (timeout <= TimeSpan.Zero) throw new InvalidOperationException(\"timeout must be positive\");\n// runtime: instrument the delegate's own latency budget so the timeout reflects p99","typeGuard":"static bool IsTimeoutRejected(Exception e) => e is TimeoutRejectedException;","tryCatchPattern":"try\n{\n    await policy.ExecuteAsync(async ct => await work(ct), cancellationToken);\n}\ncatch (TimeoutRejectedException ex)\n{\n    logger.LogWarning(ex, \"delegate exceeded timeout {Timeout}\", ex.Timeout);\n    // degrade gracefully, return cached/default, or rethrow to an outer fallback\n}","preventionTips":["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."],"tags":["runtime","timeout","cancellation","timeout-rejected","legacy-api","polly-v7"],"backgroundTag":null,"analyzedSha":"d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac","analyzedAt":"2026-08-13T16:36:01.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}