{"record":{"id":"b2fec1cf3f6fd27e","repo":"python/cpython","slug":"ssl-handshake-timeout-is-only-meaningful-with-ssl","errorCode":null,"errorMessage":"ssl_handshake_timeout is only meaningful with ssl","messagePattern":"ssl_handshake_timeout is only meaningful with ssl","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/base_events.py","lineNumber":1106,"sourceCode":"\n        if server_hostname is None and ssl:\n            # Use host as default for server_hostname.  It is an error\n            # if host is empty or not set, e.g. when an\n            # already-connected socket was passed or when only a port\n            # is given.  To avoid this error, you can pass\n            # server_hostname='' -- this will bypass the hostname\n            # check.  (This also means that if host is a numeric\n            # IP/IPv6 address, we will attempt to verify that exact\n            # address; this will probably fail, but it is possible to\n            # create a certificate for a specific IP address, so we\n            # don't judge it here.)\n            if not host:\n                raise ValueError('You must set server_hostname '\n                                 'when using ssl without a host')\n            server_hostname = host\n\n        if ssl_handshake_timeout is not None and not ssl:\n            raise ValueError(\n                'ssl_handshake_timeout is only meaningful with ssl')\n\n        if ssl_shutdown_timeout is not None and not ssl:\n            raise ValueError(\n                'ssl_shutdown_timeout is only meaningful with ssl')\n\n        if sock is not None:\n            _check_ssl_socket(sock)\n\n        if happy_eyeballs_delay is not None and interleave is None:\n            # If using happy eyeballs, default to interleave addresses by family\n            interleave = 1\n\n        if host is not None or port is not None:\n            if sock is not None:\n                raise ValueError(\n                    'host/port and sock can not be specified at the same time')\n","sourceCodeStart":1088,"sourceCodeEnd":1124,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/base_events.py#L1088-L1124","documentation":"Raised by create_connection when ssl_handshake_timeout is given but ssl is not. The handshake timeout bounds how long the TLS handshake may take; it only exists in the TLS code path, so asyncio rejects it for plaintext connections as a likely configuration error.","triggerScenarios":"Calling loop.create_connection(proto, host, port, ssl_handshake_timeout=10.0) with ssl unset. Typical when a TLS config object is spread into kwargs but its ssl key ended up None/omitted.","commonSituations":"Shared kwargs dicts for both TLS and plaintext clients; disabling TLS for local testing while leaving the timeout knob in place; refactors that made ssl conditional (**tls_kwargs) without cleaning the timeout.","solutions":["Only pass ssl_handshake_timeout when ssl is truthy: include it in the same conditional branch as ssl.","If plaintext is intended, remove the timeout argument.","Group all TLS-only kwargs (ssl, server_hostname, ssl_handshake_timeout, ssl_shutdown_timeout) so they are set or omitted together."],"exampleFix":"// before\nawait loop.create_connection(proto, h, p, ssl_handshake_timeout=10)  # no ssl\n\n// after\nif ctx is not None:\n    t, p_ = await loop.create_connection(proto, h, p, ssl=ctx,\n                                         ssl_handshake_timeout=10)\nelse:\n    t, p_ = await loop.create_connection(proto, h, p)","handlingStrategy":"validation","validationCode":"tls_kwargs = {}\nif ctx is not None:\n    tls_kwargs.update(ssl=ctx, ssl_handshake_timeout=10, ssl_shutdown_timeout=10)","typeGuard":null,"tryCatchPattern":"try:\n    await loop.create_connection(proto, h, p, ssl_handshake_timeout=10)\nexcept ValueError:\n    await loop.create_connection(proto, h, p, ssl=ssl.create_default_context(),\n                                 ssl_handshake_timeout=10)","preventionTips":["Treat ssl_handshake_timeout as TLS-only config and gate it behind the TLS flag.","Pop TLS-only keys when downgrading to plaintext: kwargs.pop('ssl_handshake_timeout', None).","Keep one shared kwargs builder for connections so TLS args cannot leak into plaintext calls."],"tags":["asyncio","tls","ssl","timeout","validation"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}