🔧 Error Fixes
· 2 min read
Last updated on

TaskCanceledException: A Task Was Canceled — How to Fix It (C#)


System.Threading.Tasks.TaskCanceledException: A task was canceled

What causes this

An async operation was canceled before it completed. In C#, this happens in two scenarios:

  • Timeout: HttpClient has a default 100-second timeout. If the server doesn’t respond in time, the request is canceled.
  • Explicit cancellation: A CancellationToken was triggered, usually because the user navigated away, the request was aborted, or a parent operation was canceled.

The tricky part: both cases throw TaskCanceledException, but they mean different things and should be handled differently.

Fix 1: Increase the HttpClient timeout

var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(300); // 5 minutes

var response = await client.GetAsync("https://slow-api.example.com/data");

For specific requests that need more time:

using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
var response = await client.GetAsync(url, cts.Token);

Fix 2: Distinguish timeout from cancellation

try
{
    var response = await client.GetAsync(url, cancellationToken);
}
catch (TaskCanceledException) when (!cancellationToken.IsCancellationRequested)
{
    // Timeout — the server didn't respond in time
    Console.WriteLine("Request timed out");
}
catch (TaskCanceledException)
{
    // Explicit cancellation — the caller canceled the operation
    Console.WriteLine("Request was canceled by the caller");
}

This pattern checks whether the cancellation came from a timeout or from the token you passed in.

Fix 3: Add retry logic

For transient timeouts, retry with exponential backoff:

using Polly;

var retryPolicy = Policy
    .Handle<TaskCanceledException>()
    .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));

var response = await retryPolicy.ExecuteAsync(() =>
    client.GetAsync(url));

Or without Polly:

for (int i = 0; i < 3; i++)
{
    try
    {
        return await client.GetAsync(url);
    }
    catch (TaskCanceledException) when (i < 2)
    {
        await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, i)));
    }
}

Fix 4: Check if the server is actually responding

The timeout might be legitimate — the server is slow or down:

curl -v https://api.example.com/endpoint

If the server is slow, consider:

  • Adding a loading indicator in the UI
  • Using streaming responses for large payloads
  • Optimizing the server-side query

Fix 5: Handle cancellation in ASP.NET

In ASP.NET controllers, the request is canceled when the client disconnects:

[HttpGet]
public async Task<IActionResult> GetData(CancellationToken cancellationToken)
{
    try
    {
        var data = await _service.GetDataAsync(cancellationToken);
        return Ok(data);
    }
    catch (OperationCanceledException)
    {
        // Client disconnected — this is normal, just log and return
        _logger.LogInformation("Client disconnected");
        return StatusCode(499); // Client Closed Request
    }
}

How to prevent it

  • Set explicit timeouts on HttpClient based on expected response times — don’t rely on the 100-second default
  • Always pass CancellationToken through your async call chain so operations can be canceled cleanly
  • Use Polly or a similar library for retry policies on external API calls
  • Log TaskCanceledException separately from other exceptions — they’re often expected behavior, not bugs Related: C# StackOverflowException — How to Fix It