python/cpython · error · ValueError

Server side SSL needs a valid SSLContext

Error message

Server side SSL needs a valid SSLContext

What it means

Raised by _create_transport_context() in asyncio.sslproto as ValueError when server_side=True but no SSLContext was supplied. Unlike clients (where ssl=True can imply a default context), a server cannot use a default context because it must be configured with the server certificate and key, so asyncio refuses the combination.

Source

Thrown at Lib/asyncio/sslproto.py:49

class AppProtocolState(enum.Enum):
    # This tracks the state of app protocol (https://git.io/fj59P):
    #
    #     INIT -cm-> CON_MADE [-dr*->] [-er-> EOF?] -cl-> CON_LOST
    #
    # * cm: connection_made()
    # * dr: data_received()
    # * er: eof_received()
    # * cl: connection_lost()

    STATE_INIT = "STATE_INIT"
    STATE_CON_MADE = "STATE_CON_MADE"
    STATE_EOF = "STATE_EOF"
    STATE_CON_LOST = "STATE_CON_LOST"


def _create_transport_context(server_side, server_hostname):
    if server_side:
        raise ValueError('Server side SSL needs a valid SSLContext')

    # Client side may pass ssl=True to use a default
    # context; in that case the sslcontext passed is None.
    # The default is secure for client connections.
    # Python 3.4+: use up-to-date strong settings.
    sslcontext = ssl.create_default_context()
    if not server_hostname:
        sslcontext.check_hostname = False
    return sslcontext


def add_flowcontrol_defaults(high, low, kb):
    if high is None:
        if low is None:
            hi = kb * 1024
        else:
            lo = low
            hi = 4 * lo

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Build a server context and pass it: ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER); ctx.load_cert_chain('cert.pem', 'key.pem'); then ssl=ctx in create_server/start_server.
  2. For quick internal servers: ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) then load_cert_chain.
  3. Never pass bare True for ssl on the server side; reserve ssl=True shorthand for client connections.

Example fix

// before
server = await asyncio.start_server(handler, '0.0.0.0', 8443, ssl=True)
# ValueError: Server side SSL needs a valid SSLContext

// after
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain('/etc/ssl/certs/server.pem', '/etc/ssl/private/server.key')
server = await asyncio.start_server(handler, '0.0.0.0', 8443, ssl=ctx)
Defensive patterns

Strategy: validation

Validate before calling

import ssl

def server_ssl_context(certfile, keyfile):
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.load_cert_chain(certfile, keyfile)
    return ctx

ctx = server_ssl_context('server.crt', 'server.key')
server = await asyncio.start_server(handler, '0.0.0.0', 8443, ssl=ctx)

Type guard

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

Try / catch

try:
    server = await asyncio.start_server(handler, host, port, ssl=ssl_arg)
except ValueError as e:
    if 'Server side SSL needs a valid SSLContext' in str(e):
        raise SystemExit('configure ssl=SSLContext with load_cert_chain, not True')
    raise

Prevention

When it happens

Trigger: loop.create_server(proto_factory, host, port, ssl=True) or asyncio.start_server(handler, host, port, ssl=True) — i.e. passing the bare truthy value ssl=True on the server side.

Common situations: Copy-pasting client examples (where ssl=True is valid) into server code; enabling TLS on a server before provisioning certificates; test servers meant to use self-signed certs where the context was never constructed.

Understand the failure class

Related errors


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