python/cpython · error · TypeError

sslcontext is expected to be an instance of ssl.SSLContext,

Error message

sslcontext is expected to be an instance of ssl.SSLContext, got {sslcontext!r}

What it means

A TypeError raised by loop.start_tls() when the sslcontext argument is not an ssl.SSLContext instance. start_tls uses the context to configure certificates, protocols and verification; passing anything else (a boolean like ssl.CERT_REQUIRED, a string path, or None) is rejected with the offending value shown.

Source

Thrown at Lib/asyncio/base_events.py:1343

            if total_sent > 0 and hasattr(file, 'seek'):
                file.seek(offset + total_sent)
            await proto.restore()

    async def start_tls(self, transport, protocol, sslcontext, *,
                        server_side=False,
                        server_hostname=None,
                        ssl_handshake_timeout=None,
                        ssl_shutdown_timeout=None):
        """Upgrade transport to TLS.

        Return a new transport that *protocol* should start using
        immediately.
        """
        if ssl is None:
            raise RuntimeError('Python ssl module is not available')

        if not isinstance(sslcontext, ssl.SSLContext):
            raise TypeError(
                f'sslcontext is expected to be an instance of ssl.SSLContext, '
                f'got {sslcontext!r}')

        if not getattr(transport, '_start_tls_compatible', False):
            raise TypeError(
                f'transport {transport!r} is not supported by start_tls()')

        waiter = self.create_future()
        ssl_protocol = sslproto.SSLProtocol(
            self, protocol, sslcontext, waiter,
            server_side, server_hostname,
            ssl_handshake_timeout=ssl_handshake_timeout,
            ssl_shutdown_timeout=ssl_shutdown_timeout,
            call_connection_made=False)

        # Pause early so that "ssl_protocol.data_received()" doesn't
        # have a chance to get called before "ssl_protocol.connection_made()".
        transport.pause_reading()

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Create a real context: ctx = ssl.create_default_context() (client) or ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) with load_cert_chain (server).
  2. Set verify options on the context (ctx.verify_mode = ssl.CERT_REQUIRED), not as the argument itself.
  3. Type-check the variable early if it comes from dynamic configuration.

Example fix

# before
transport = await loop.start_tls(tp, proto, ssl.CERT_REQUIRED)  # TypeError

# after
ctx = ssl.create_default_context()
ctx.check_hostname = True
transport = await loop.start_tls(tp, proto, ctx)
Defensive patterns

Strategy: type-guard

Validate before calling

import ssl
assert isinstance(ctx, ssl.SSLContext), 'start_tls requires an ssl.SSLContext'

Type guard

import ssl

def is_ssl_context(obj: object) -> bool:
    return isinstance(obj, ssl.SSLContext)

Try / catch

try:
    tp = await loop.start_tls(raw_tp, proto, ctx)
except TypeError as e:
    if 'ssl.SSLContext' not in str(e):
        raise
    ctx = ssl.create_default_context()
    tp = await loop.start_tls(raw_tp, proto, ctx)

Prevention

When it happens

Trigger: Calling loop.start_tls(transport, protocol, ssl.CERT_REQUIRED) — passing a verify-mode constant instead of a context; passing a PEM filename; passing None expecting a default context (there is none).

Common situations: Confusing ssl module constants with contexts (CERT_REQUIRED etc. are ints); porting code that used ssl.wrap_socket's looser arguments; forgetting ssl.create_default_context() when building the call.

Understand the failure class

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/741f69adc170f3be. Report an issue: GitHub.