psf/requests · error · ValueError
Invalid timeout {timeout}. Pass a (connect, read) timeout tu
Error message
Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, or a single float to set both timeouts to the same value. What it means
In HTTPAdapter.send, a tuple timeout must unpack into exactly two values, (connect, read), which are wrapped into urllib3's Timeout (TimeoutSauce). If tuple unpacking raises ValueError — wrong number of elements — requests re-raises with this explicit message. Non-tuple values are treated as a single timeout applied to both connect and read, so only malformed tuples hit this branch.
Source
Thrown at src/requests/adapters.py:686
self.cert_verify(conn, request.url, verify, cert)
url = self.request_url(request, proxies)
self.add_headers(
request,
stream=stream,
timeout=timeout,
verify=verify,
cert=cert,
proxies=proxies,
)
chunked = not (request.body is None or "Content-Length" in request.headers)
if isinstance(timeout, tuple):
try:
connect, read = timeout
resolved_timeout = TimeoutSauce(connect=connect, read=read)
except ValueError:
raise ValueError(
f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, "
f"or a single float to set both timeouts to the same value."
)
elif isinstance(timeout, TimeoutSauce):
resolved_timeout = timeout
else:
resolved_timeout = TimeoutSauce(connect=timeout, read=timeout)
try:
resp = conn.urlopen(
method=request.method,
url=url,
body=request.body, # type: ignore[arg-type] # urllib3 stubs don't accept Iterable[bytes | str]
headers=request.headers, # type: ignore[arg-type] # urllib3#3072
redirect=False,
assert_same_host=False,
preload_content=False,
decode_content=False,View on GitHub ↗ (pinned to 414f0513c3)
Solutions
- Pass exactly two elements: timeout=(connect_timeout, read_timeout), e.g. timeout=(3.05, 27)
- For one value applied to both, pass a plain float — timeout=30, not timeout=(30,)
- For total-deadline semantics, construct urllib3.util.Timeout(connect=..., read=..., total=...) and pass that as timeout
Example fix
# before requests.get(url, timeout=(3, 10, 30)) # 3-tuple -> ValueError # after requests.get(url, timeout=(3, 10)) # or a total deadline: from urllib3.util import Timeout requests.get(url, timeout=Timeout(connect=3, read=10, total=30))
Defensive patterns
Strategy: type-guard
Validate before calling
def validate_timeout(timeout):
if timeout is None or isinstance(timeout, (int, float)):
return timeout
if isinstance(timeout, tuple) and len(timeout) == 2 and all(
t is None or isinstance(t, (int, float)) for t in timeout
):
return timeout
raise ValueError(f"timeout must be float or (connect, read) tuple, got {timeout!r}") Type guard
def is_valid_timeout(value):
if value is None or isinstance(value, (int, float)):
return True
return (isinstance(value, tuple) and len(value) == 2
and all(t is None or isinstance(t, (int, float)) for t in value)) Try / catch
try:
resp = session.get(url, timeout=timeout)
except ValueError as e:
if "Invalid timeout" in str(e):
raise ConfigError(f"Bad timeout value: {timeout!r}") from e
raise Prevention
- Never pass a 3-tuple or a string from config as timeout; convert config values to float or (float, float) explicitly
- Always set an explicit timeout — requests defaults to none, which hangs forever
- Wrap timeout handling in one shared session factory so the shape is validated once
When it happens
Trigger: requests.get(url, timeout=(3,)) or timeout=(3, 5, 10) — any tuple whose length isn't 2. Raised at src/requests/adapters.py:681-689. Note timeout=(3, 5) is valid, and a 3-tuple mimicking other libraries' (connect, read, total) form fails here.
Common situations: Porting code from libraries that accept a total/3-element timeout; a trailing comma accidentally turning a number into a 1-tuple (timeout=(30,)); config systems delivering timeout lists/tuples of the wrong arity; confusing urllib3's Timeout(total=...) capabilities with requests' 2-tuple contract.
Related errors
- Unsupported event specified, with event name "{event}"
- Invalid URL {url!r}: No host supplied
- Could not find a suitable TLS CA certificate bundle, invalid
- Could not find the TLS certificate file, invalid path: {conn
- Could not find the TLS key file, invalid path: {conn.key_fil
AI-assisted analysis of psf/requests@414f0513c3 (2026-07-31).
Data as JSON: /data/errors/64ab0e48615e4134.json.
Report an issue: GitHub ↗.