{"id":"64ab0e48615e4134","repo":"psf/requests","slug":"invalid-timeout-timeout-pass-a-connect-read","errorCode":null,"errorMessage":"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, or a single float to set both timeouts to the same value.","messagePattern":"Invalid timeout (.+?)\\. Pass a \\(connect, read\\) timeout tuple, or a single float to set both timeouts to the same value\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/requests/adapters.py","lineNumber":686,"sourceCode":"        self.cert_verify(conn, request.url, verify, cert)\n        url = self.request_url(request, proxies)\n        self.add_headers(\n            request,\n            stream=stream,\n            timeout=timeout,\n            verify=verify,\n            cert=cert,\n            proxies=proxies,\n        )\n\n        chunked = not (request.body is None or \"Content-Length\" in request.headers)\n\n        if isinstance(timeout, tuple):\n            try:\n                connect, read = timeout\n                resolved_timeout = TimeoutSauce(connect=connect, read=read)\n            except ValueError:\n                raise ValueError(\n                    f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n                    f\"or a single float to set both timeouts to the same value.\"\n                )\n        elif isinstance(timeout, TimeoutSauce):\n            resolved_timeout = timeout\n        else:\n            resolved_timeout = TimeoutSauce(connect=timeout, read=timeout)\n\n        try:\n            resp = conn.urlopen(\n                method=request.method,\n                url=url,\n                body=request.body,  # type: ignore[arg-type]  # urllib3 stubs don't accept Iterable[bytes | str]\n                headers=request.headers,  # type: ignore[arg-type]  # urllib3#3072\n                redirect=False,\n                assert_same_host=False,\n                preload_content=False,\n                decode_content=False,","sourceCodeStart":668,"sourceCodeEnd":704,"githubUrl":"https://github.com/psf/requests/blob/414f0513c33883adf6f2b46901d4f0b38a455851/src/requests/adapters.py#L668-L704","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nrequests.get(url, timeout=(3, 10, 30))  # 3-tuple -> ValueError\n\n# after\nrequests.get(url, timeout=(3, 10))\n# or a total deadline:\nfrom urllib3.util import Timeout\nrequests.get(url, timeout=Timeout(connect=3, read=10, total=30))","handlingStrategy":"type-guard","validationCode":"def validate_timeout(timeout):\n    if timeout is None or isinstance(timeout, (int, float)):\n        return timeout\n    if isinstance(timeout, tuple) and len(timeout) == 2 and all(\n        t is None or isinstance(t, (int, float)) for t in timeout\n    ):\n        return timeout\n    raise ValueError(f\"timeout must be float or (connect, read) tuple, got {timeout!r}\")","typeGuard":"def is_valid_timeout(value):\n    if value is None or isinstance(value, (int, float)):\n        return True\n    return (isinstance(value, tuple) and len(value) == 2\n            and all(t is None or isinstance(t, (int, float)) for t in value))","tryCatchPattern":"try:\n    resp = session.get(url, timeout=timeout)\nexcept ValueError as e:\n    if \"Invalid timeout\" in str(e):\n        raise ConfigError(f\"Bad timeout value: {timeout!r}\") from e\n    raise","preventionTips":["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"],"tags":["timeout","api-usage","python","configuration"],"analyzedSha":"414f0513c33883adf6f2b46901d4f0b38a455851","analyzedAt":"2026-07-31T19:24:57.696Z","schemaVersion":2}