I m a bit puzzled with this. I get an occasional task cancelled exception on the following piece of code. This is not because it times out - it happens too soon.
Assume the MakeRequest in the code below formulates the URL and simply returns with
return await _client.SendAsync(request, cancellationToken).ConfigureAwait(false);
The rest of the code gets the response and makes sure its successful otherwise throws.
protected async Task<T> MakeRequestAndResponseAsync<T>(
HttpRequestMessage request,
CancellationToken cancellationToken = default(CancellationToken))
{
var response = await MakeRequest(request, cancellationToken).ConfigureAwait(false);
return await GetResponse<T>(response, throwsOnNotFound).ConfigureAwait(false);
}
protected async Task<T> GetResponse<T>(HttpResponseMessage response, Boolean throwsOnNotFound = true)
{
String content = await EnsureSuccessfulResponse(response, throwsOnNotFound).ConfigureAwait(false);
if (content == null && !throwsOnNotFound) return default(T);
return JsonConvert.DeserializeObject<T>(content);
}
protected async Task<String> EnsureSuccessfulResponse(HttpResponseMessage response, Boolean throwsOnNotFound = true)
{
String content = string.Empty;
if (response.Content != null)
{
content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
if (response.IsSuccessStatusCode) return content;
var error = JsonConvert.DeserializeObject<ApiErrorResponse>(content);
throw new ApiErrorException(/*some internal stuff*/);
}
The whole code is invoked from a Console app like this. This is a multithreaded application and so this code may be invoked multiple times simultaneously.
var result = MakeRequestAndResponseAsync(...).Result;
// do something with result
What I have crossed out so far:
- The HttpClient is correctly initialised and re-used. It doesnt get disposed mid-way.
- This error happens sometimes. Those requests happen in parallel so I thought the HttpClient SendAsync is not thread-safe but I discovered here that in fact it is thread-safe
- It's not a timeout exception. I have a unique id per request and i can see in the logs when it starts and when it errors (approx 1-2 seconds)
What I don't know: - If the other service returns a non-200 I intentionally throw a custom exception (ApiErrorException). I suspect it may be related to that, i.e. that the task is cancelled when it throws. If thats the case I would expect to get back the ApiErrorException rather than a task cancelled exception. I unwrap all inner exceptions to the point there isn't anything left. Whats left is the TaskCancelled exception.
Any help would be greatly appreciated.
Thanks