VoidSharp
VoidSharp

Reputation: 33

Why is my Polly Retry Policy is throwing "caught" exceptions anyway (HttpClient.Timeout)

Below I am showcasing the policy I am using to handle a PostAsync() request using a HttpClient. However, it seems like even though I passed generic Exception, it still decides to throw it and fail to retry. I would like to ask why this is, and any workarounds.

HttpsClient Setup

_httpClientHandler = new HttpClientHandler()
{
    UseCookies = true,
    CookieContainer = new CookieContainer(),
    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
_httpClient = new HttpClient(_httpClientHandler) { Timeout = TimeSpan.FromSeconds(10) };

Post Request

private async Task PreparePayloadByDate(DateTime date)
{
    // Define the retry policy with exponential backoff
    var retryPolicy = Policy<HttpResponseMessage>.Handle<HttpRequestException>()
        .Or<TaskCanceledException>()
        .OrResult(r => !r.IsSuccessStatusCode)
        .WaitAndRetryForeverAsync(
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
            onRetry: (exception, retryCount, timeSpan) =>
            {
                _logger.Log(LogEnums.Error, $"Attempt {retryCount} failed. Retrying in {timeSpan} seconds...");
            });
    var stringPayload = JsonConvert.SerializeObject(new { key = "days", options = date.ToString("yyyy-MM-dd") });
    var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
    var r1 = await retryPolicy.ExecuteAsync(() =>
        _httpClient.PostAsync($"{_moduleConfig.Endpoint}/options/selected", httpContent));
}

I have also tried using a Policy Timeout rather than the HttpClient's Timeout. This seemed to have the same outcome. See Answer for more context

I would also like to add that this RetryPolicy is inside a try catch, and the exception being thrown is The request was canceled due to the configured HttpClient.Timeout of 15 seconds elapsing. with a StackTrace of at System.Net.Http.HttpClient.HandleFailure(~).

Additional Notes (Added on 2/1/2025)

Even when the timeout of the HttpClient is set to 3 minutes, 10 minutes, etc. It always will timeout. I believe its due to the url I am posting through rate limiting but not really sure. I can not get it to replicate in a browser.

Upvotes: 0

Views: 64

Answers (1)

Guru Stron
Guru Stron

Reputation: 143098

TBH was not able to repro the issue, for me your code catches the timeout, but in general your code is not shielded from the exceptions since r2.EnsureSuccessStatusCode(); will throw for non-successes. In general you might want to handle results too in your policy. For example (for testing purposes):

var retryPolicy = Policy<HttpResponseMessage>
    .Handle<Exception>()
    .OrResult(r => !r.IsSuccessStatusCode)
    .WaitAndRetryForeverAsync(
        retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), 
        onRetry: (exceptionOrResult, timeSpan) => ...);

Also you can use Microsoft.Extensions.Http.Polly which has some default setup and convenient methods:

var policy = HttpPolicyExtensions
    .HandleTransientHttpError()
    .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
    .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2,
        retryAttempt)));

See the Implement HTTP call retries with exponential backoff with IHttpClientFactory and Polly policies doc.

Upvotes: 1

Related Questions