Invoke-WebRequest and WebClient timeout in PowerShell
System.Net.WebClient has no Timeout property. Use -TimeoutSec on Invoke-WebRequest and Invoke-RestMethod, set Timeout on HttpClient, or subclass WebClient.
# Web cmdlets: seconds, 0 (default) = no timeout
Invoke-WebRequest -Uri 'https://example.com/' -UseBasicParsing -TimeoutSec 30
Invoke-RestMethod -Uri 'https://example.com/' -TimeoutSec 30
# HttpClient: set Timeout before the first request, later it throws
Add-Type -AssemblyName System.Net.Http
$http = [System.Net.Http.HttpClient]::new()
$http.Timeout = [TimeSpan]::FromSeconds(30)
$html = $http.GetStringAsync('https://example.com/').GetAwaiter().GetResult()
WebClient timeout: subclass it with Add-Type
Override GetWebRequest to set timeouts on every request:
Add-Type -IgnoreWarnings -TypeDefinition @"
using System;
using System.Net;
public class TimeoutWebClient : WebClient
{
public int TimeoutMs = 30000;
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request != null)
{
request.Timeout = TimeoutMs;
HttpWebRequest http = request as HttpWebRequest;
if (http != null) { http.ReadWriteTimeout = TimeoutMs; }
}
return request;
}
}
"@
$wc = New-Object TimeoutWebClient
$wc.TimeoutMs = 10000 # milliseconds
$wc.DownloadString('https://example.com/')
Why it looks like this:
Timeoutlimits the wait for the response (default 100 seconds),ReadWriteTimeouteach read of the body (default 5 minutes). In my test, an 8-second pause after headers tripped a 3-secondReadWriteTimeout, but notTimeout.- PowerShell 7 needs
-IgnoreWarnings:WebClientis obsolete in .NET 6+ (SYSLIB0014), andAdd-Typetreats warnings as errors, failing withCannot add type. Compilation errors occurred. - The C# avoids newer syntax so the same block compiles in Windows PowerShell 5.1 with its old CodeDom compiler. A type can't be redefined in a session: after editing the class, rename it or restart PowerShell.
Invoke-WebRequest -TimeoutSec: what it limits
-TimeoutSec covers only the wait for response headers: DNS, connecting and server think time, not the body. With -TimeoutSec 3, an 8-second body after instant headers completed fine.
PowerShell 7.4 renamed the parameter to -ConnectionTimeoutSeconds (-TimeoutSec stays as an alias) and added -OperationTimeoutSeconds, the longest allowed pause between chunks of the body. For downloads, set both:
Invoke-WebRequest -Uri $url -OutFile $file -ConnectionTimeoutSeconds 15 -OperationTimeoutSeconds 60
Neither caps the total time; HttpClient.Timeout does for GetStringAsync, body included.
Two more details:
-MaximumRetryCountdoesn't retry timeouts:-TimeoutSec 2 -MaximumRetryCount 2failed after 2 seconds.- Windows PowerShell 5.1 has only
-TimeoutSec, and a slow DNS lookup can delay it to 15 seconds or more (docs); PowerShell 7 isn't affected. Add-UseBasicParsingin 5.1: since the December 2025 security update,Invoke-WebRequestwithout it stops at a confirmation prompt.
"The operation has timed out" and DownloadDataInternal
Timeout on Windows PowerShell 5.1 or .NET Framework:
Exception calling "DownloadString" with "1" argument(s): "The operation has timed out"
System.Net.WebException: The operation has timed out
at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
at System.Net.WebClient.DownloadString(Uri address)
PowerShell 7.5 adds a period and the HttpWebRequest.GetResponse(), GetWebResponse and DownloadBits frames on top.
On .NET Framework DownloadDataInternal tops every DownloadString and DownloadData error (throw e; resets the trace), so read the message line:
- "The operation has timed out": no response within
HttpWebRequest.Timeout(100 seconds by default). Change it with the subclass above or use the cmdlets. For ASP.NET behind a proxy, see web.config proxy settings. - "(407) Proxy Authentication Required": set
$wc.Proxy.Credentials = [Net.CredentialCache]::DefaultNetworkCredentials, oruseDefaultCredentials="true"on<defaultProxy>in web.config. - "Could not create SSL/TLS secure channel": run
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 'Tls12'first.
Catch a timeout by type and check Status:
try {
$wc.DownloadString($url)
}
catch [System.Net.WebException] {
if ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::Timeout) {
Write-Warning "Timed out: $url"
}
else { throw }
}
A stalled body (ReadWriteTimeout) in PowerShell 7 throws "An exception occurred during a WebClient request." with Status UnknownError, which the if misses; add -or $_.Exception.InnerException -is [System.IO.IOException].
Invoke-WebRequest in PowerShell 7 throws TaskCanceledException ("The request was canceled due to the configured HttpClient.Timeout of 30 seconds elapsing.") on the connection timeout and TimeoutException on the operation timeout (inside AggregateException with -OutFile):
try {
Invoke-RestMethod -Uri $url -TimeoutSec 30
}
catch [System.Threading.Tasks.TaskCanceledException] {
Write-Warning "No response in 30 seconds: $url"
}
Windows PowerShell 5.1 throws WebException instead (docs): use the WebClient pattern there.
Async methods ignore Timeout in Windows PowerShell 5.1
In .NET Framework, HttpWebRequest.Timeout has no effect on asynchronous requests, and DownloadStringAsync, DownloadFileAsync and their TaskAsync versions run on BeginGetResponse. In PowerShell 7, DownloadStringTaskAsync respected a 3-second timeout in my test. On 5.1, use synchronous methods or HttpClient.
C# WebClient timeout
On .NET 6+, Microsoft recommends HttpClient:
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
try
{
Console.WriteLine(await http.GetStringAsync(url));
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
Console.WriteLine("Timed out");
}
Legacy code stuck on WebClient:
class TimeoutWebClient : WebClient
{
public int TimeoutMs { get; set; } = 30_000;
protected override WebRequest GetWebRequest(Uri address)
{
var request = base.GetWebRequest(address);
request.Timeout = TimeoutMs;
if (request is HttpWebRequest http) http.ReadWriteTimeout = TimeoutMs;
return request;
}
}
On .NET 6+, add #pragma warning disable SYSLIB0014.