{"record":{"id":"75bd2c8715109a33","repo":"python/cpython","slug":"ssl-handshake-timeout-should-be-a-positive-number","errorCode":null,"errorMessage":"ssl_handshake_timeout should be a positive number, got {ssl_handshake_timeout}","messagePattern":"ssl_handshake_timeout should be a positive number, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/sslproto.py","lineNumber":284,"sourceCode":"    _handshake_start_time = None\n    _handshake_timeout_handle = None\n    _shutdown_timeout_handle = None\n\n    def __init__(self, loop, app_protocol, sslcontext, waiter,\n                 server_side=False, server_hostname=None,\n                 call_connection_made=True,\n                 ssl_handshake_timeout=None,\n                 ssl_shutdown_timeout=None):\n        if ssl is None:\n            raise RuntimeError(\"stdlib ssl module not available\")\n\n        self._ssl_buffer = bytearray(self.max_size)\n        self._ssl_buffer_view = memoryview(self._ssl_buffer)\n\n        if ssl_handshake_timeout is None:\n            ssl_handshake_timeout = constants.SSL_HANDSHAKE_TIMEOUT\n        elif ssl_handshake_timeout <= 0:\n            raise ValueError(\n                f\"ssl_handshake_timeout should be a positive number, \"\n                f\"got {ssl_handshake_timeout}\")\n        if ssl_shutdown_timeout is None:\n            ssl_shutdown_timeout = constants.SSL_SHUTDOWN_TIMEOUT\n        elif ssl_shutdown_timeout <= 0:\n            raise ValueError(\n                f\"ssl_shutdown_timeout should be a positive number, \"\n                f\"got {ssl_shutdown_timeout}\")\n\n        if not sslcontext:\n            sslcontext = _create_transport_context(\n                server_side, server_hostname)\n\n        self._server_side = server_side\n        if server_hostname and not server_side:\n            self._server_hostname = server_hostname\n        else:\n            self._server_hostname = None","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/sslproto.py#L266-L302","documentation":"Raised in _SSLProtocol.__init__ as ValueError when an explicitly supplied ssl_handshake_timeout is <= 0 (and not None). The timeout bounds how long the TLS handshake may take before the connection is aborted; zero or negative durations are meaningless and rejected at construction.","triggerScenarios":"Passing ssl_handshake_timeout=0 or a negative number to loop.create_connection()/open_connection()/create_server()/start_tls (the kwarg is forwarded to _SSLProtocol).","commonSituations":"Using 0 as an 'infinite/disabled' sentinel (the actual way to get the default is None); configuration values loaded from files/env where an unset variable parses as 0; unit tests parameterizing timeouts including a 0 case.","solutions":["Pass a positive number of seconds, e.g. ssl_handshake_timeout=10.","Pass None (or omit the kwarg) to use the default (constants.SSL_HANDSHAKE_TIMEOUT, 60s).","Validate config-sourced timeout values (if v is not None and v <= 0: raise) before handing them to asyncio."],"exampleFix":"// before\nawait asyncio.open_connection('h', 443, ssl=ctx, ssl_handshake_timeout=0)\n# ValueError\n\n// after\nawait asyncio.open_connection('h', 443, ssl=ctx, ssl_handshake_timeout=10.0)","handlingStrategy":"validation","validationCode":"def handshake_timeout(value):\n    if value is None:\n        return None  # asyncio default (60s)\n    value = float(value)\n    if value <= 0:\n        raise ValueError('ssl_handshake_timeout must be > 0')\n    return value\n\nawait asyncio.open_connection('h', 443, ssl=ctx,\n                              ssl_handshake_timeout=handshake_timeout(cfg))","typeGuard":"def is_positive_timeout(v) -> bool:\n    return v is None or (isinstance(v, (int, float)) and not isinstance(v, bool) and v > 0)","tryCatchPattern":"try:\n    await asyncio.open_connection('h', 443, ssl=ctx, ssl_handshake_timeout=t)\nexcept ValueError as e:\n    if 'ssl_handshake_timeout' in str(e):\n        await asyncio.open_connection('h', 443, ssl=ctx)  # use default\n    else:\n        raise","preventionTips":["Pass None for the default timeout; never 0 as a 'disable' sentinel.","Validate config-sourced numbers (env vars, YAML) as positive floats at load time.","Keep timeout tuning centralized so bad values are caught in one validator."],"tags":["asyncio","ssl","timeout","configuration","valueerror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}