{"record":{"id":"c80450d793e79aac","repo":"python/cpython","slug":"transport-transport-r-is-not-supported-by-start","errorCode":null,"errorMessage":"transport {transport!r} is not supported by start_tls()","messagePattern":"transport (.+?) is not supported by start_tls\\(\\)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/base_events.py","lineNumber":1348,"sourceCode":"                        server_side=False,\n                        server_hostname=None,\n                        ssl_handshake_timeout=None,\n                        ssl_shutdown_timeout=None):\n        \"\"\"Upgrade transport to TLS.\n\n        Return a new transport that *protocol* should start using\n        immediately.\n        \"\"\"\n        if ssl is None:\n            raise RuntimeError('Python ssl module is not available')\n\n        if not isinstance(sslcontext, ssl.SSLContext):\n            raise TypeError(\n                f'sslcontext is expected to be an instance of ssl.SSLContext, '\n                f'got {sslcontext!r}')\n\n        if not getattr(transport, '_start_tls_compatible', False):\n            raise TypeError(\n                f'transport {transport!r} is not supported by start_tls()')\n\n        waiter = self.create_future()\n        ssl_protocol = sslproto.SSLProtocol(\n            self, protocol, sslcontext, waiter,\n            server_side, server_hostname,\n            ssl_handshake_timeout=ssl_handshake_timeout,\n            ssl_shutdown_timeout=ssl_shutdown_timeout,\n            call_connection_made=False)\n\n        # Pause early so that \"ssl_protocol.data_received()\" doesn't\n        # have a chance to get called before \"ssl_protocol.connection_made()\".\n        transport.pause_reading()\n\n        # gh-142352: move buffered StreamReader data to SSLProtocol\n        if server_side:\n            from .streams import StreamReaderProtocol\n            if isinstance(protocol, StreamReaderProtocol):","sourceCodeStart":1330,"sourceCodeEnd":1366,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/base_events.py#L1330-L1366","documentation":"Raised by BaseEventLoop.start_tls() when the transport passed in does not set the _start_tls_compatible attribute to True. start_tls() can only upgrade an existing plain TCP transport (from create_connection/create_server accept) to TLS; datagram transports, pipe/subprocess transports, and already-encrypted SSL transports cannot be upgraded.","triggerScenarios":"Calling loop.start_tls(transport, protocol, sslcontext) where transport is a datagram transport (create_datagram_endpoint), a _SelectorTransport pipe/stdin/stdout transport, an already-wrapped SSL transport, or a custom transport class that does not define _start_tls_compatible = True.","commonSituations":"Attempting STARTTLS-style upgrades (e.g. smtp, redis TLS upgrade) on a connection that is already TLS, or on a UDP-based protocol; passing the raw transport obtained from a transport/protocol pair that is not a socket stream transport; custom transport subclasses that forget the compatibility flag.","solutions":["Verify the transport came from loop.create_connection() or an accepted TCP server connection before calling start_tls()","If the connection is already TLS, do not call start_tls() again; use the existing SSL transport or reconnect with ssl= passed to create_connection()","For custom transports, set _start_tls_compatible = True only if the transport is a plain socket stream compatible with the selector implementation","Check the protocol stack: unwrapping TLS (transport.get_extra_info('ssl_object') / unwrap) then re-wrapping is not supported by start_tls()"],"exampleFix":"// before\ntransport, protocol = await loop.create_datagram_endpoint(...)  # UDP\nawait loop.start_tls(transport, protocol, ctx)  # TypeError\n\n// after\ntransport, protocol = await loop.create_connection(factory, host, port)  # TCP\n tls_transport = await loop.start_tls(transport, protocol, ctx)","handlingStrategy":"validation","validationCode":"def can_start_tls(transport) -> bool:\n    return bool(getattr(transport, '_start_tls_compatible', False))\n\nif not can_start_tls(transport):\n    raise RuntimeError(f'cannot upgrade {transport!r}; reconnect with ssl= instead')\ntls_transport = await loop.start_tls(transport, protocol, ctx)","typeGuard":"def is_start_tls_compatible(t: asyncio.Transport) -> TypeGuard[asyncio.Transport]:\n    return getattr(t, '_start_tls_compatible', False) is True","tryCatchPattern":"try:\n    tls_t = await loop.start_tls(transport, protocol, ctx)\nexcept TypeError as e:\n    if 'not supported by start_tls' in str(e):\n        transport.close()  # fall back: reconnect with ssl= from the start\n    else:\n        raise","preventionTips":["Only call start_tls() on transports returned by create_connection() or accepted TCP sockets","Track whether a connection is already TLS (e.g. transport.get_extra_info('ssl_object') is not None) and never double-upgrade","For custom transports, set _start_tls_compatible only after verifying selector-based stream semantics"],"tags":["asyncio","tls","transport","start-tls"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}