How to change the HttpClient timeout per request in C#
By FoxLearn 1/21/2025 9:32:01 AM 37
The best way to achieve this is by utilizing a CancellationToken
, which allows you to set a custom timeout for each individual request without affecting the overall timeout of the HttpClient
.
using (var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3))) { try { var response = await httpClient.GetAsync(uri, tokenSource.Token); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); return content; } catch (TaskCanceledException) { Console.WriteLine("Request timed out"); } }
You can’t modify HttpClient.Timeout
once the instance is in use. Instead, you can use a CancellationToken
to handle per-request timeouts. The actual timeout used is the smaller of HttpClient.Timeout
and the timeout specified by the CancellationToken
.
For example:
- If
HttpClient.Timeout
is 10 seconds, but you set theCancellationToken
timeout to 3 seconds, the timeout will be 3 seconds. - If you set the
CancellationToken
timeout to 12 seconds, the timeout will be 10 seconds (as it is the smaller value).
If you attempt to modify HttpClient.Timeout
after the instance has been used for the first request, you'll encounter the following exception:
InvalidOperationException: This instance has already started one or more requests and can only be modified before sending the first request.
When controlling timeouts, remember that since you can't change HttpClient.Timeout
after the instance is used, you cannot set it to a value larger than HttpClient.Timeout
. Therefore, if you are using CancellationTokens
for per-request timeouts, ensure that HttpClient.Timeout
is set to a value greater than any maximum timeout you intend to use. (By default, HttpClient.Timeout
is 100 seconds.)
CancellationToken Timeout Less than HttpClient Timeout
static async Task Main(string[] args) { string uri = "https://localhost:5000/stocks/VTSAX"; var requestTimeout = TimeSpan.FromSeconds(1); var httpTimeout = TimeSpan.FromSeconds(5); HttpClient httpClient = new HttpClient(); httpClient.Timeout = httpTimeout; var stopwatch = Stopwatch.StartNew(); try { using (var tokenSource = new CancellationTokenSource(requestTimeout)) { var response = await httpClient.GetAsync(uri, tokenSource.Token); } } catch (TaskCanceledException) { Console.WriteLine($"Timed out after {stopwatch.Elapsed}"); } }
This indicates the request used the 1-second timeout specified by the CancellationToken
.
CancellationToken Timeout Greater than HttpClient Timeout
Now, change the CancellationToken
timeout to a greater value:
var requestTimeout = TimeSpan.FromSeconds(10); var httpTimeout = TimeSpan.FromSeconds(5);
This shows that the request used HttpClient.Timeout
, which was 5 seconds, as the CancellationToken
timeout was greater.
Avoid Invalid Timeouts
When creating a CancellationTokenSource
, be cautious not to pass invalid timeout values. A timeout of 0
will cause an immediate timeout:
new CancellationTokenSource(TimeSpan.FromSeconds(0));
If you pass a negative timeout, you’ll encounter an exception:
new CancellationTokenSource(TimeSpan.FromSeconds(-1));
This will throw:
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter ‘delay’)
To avoid unexpected behavior, you should verify the timeout value is valid before creating the CancellationTokenSource
:
if (requestTimeout.TotalSeconds > 0) { using (var tokenSource = new CancellationTokenSource(requestTimeout)) { var response = await httpClient.GetAsync(uri, tokenSource.Token); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); return content; } }
In cases where you want the user to be able to cancel the request manually while also specifying a custom timeout, you can combine multiple CancellationToken
instances using CancellationTokenSource.CreateLinkedTokenSource()
.
public async Task<string> FetchDataAsync(string uri, TimeSpan timeout, CancellationToken userCancellationToken) { try { using (var timeoutCts = new CancellationTokenSource(timeout)) using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, userCancellationToken)) { var response = await httpClient.GetAsync(uri, linkedCts.Token); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } } catch (TaskCanceledException) { if (userCancellationToken.IsCancellationRequested) { Console.WriteLine("Operation canceled by user."); } else { Console.WriteLine("Request timed out."); } throw; } }
This method allows you to handle both the timeout and user cancellation gracefully, distinguishing between the two causes when either occurs.
Sometimes HttpClient.Timeout
might seem to be ignored if automatic proxy detection is slow. Disabling proxy detection can resolve this:
In .NET Framework, disable proxy detection in the app.config
or web.config
<system.net> <defaultProxy> <proxy bypassonlocal="true" usesystemdefault="false" /> </defaultProxy> </system.net>
In .NET Core, disable the proxy programmatically:
var handler = new HttpClientHandler { UseProxy = false }; var httpClient = new HttpClient(handler);
- How to Get and send JSON using HttpClient in C#
- How to consume an SSE endpoint using HttpClient in C#
- How to send a file using HttpClient in C#
- Sending query strings using HttpClient in C#
- Performance Boost from Reusing HttpClient Connections
- Handling Redirects with HttpClient in C#
- How to read problem details JSON using HttpClient in C#
- How to Cancel an HttpClient Request in C#