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 * loView on GitHub (pinned to bc6749cc3b)
Solutions
- 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.
- For quick internal servers: ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) then load_cert_chain.
- 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
- Reserve ssl=True for client-side calls only.
- Centralize context creation in one helper that loads cert and key.
- Add a startup smoke test that constructs the server config and fails fast on bad ssl args.
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Socket cannot be of type SSLSocket
- server_hostname is only meaningful with ssl
- You must set server_hostname when using ssl without a host
- ssl_handshake_timeout is only meaningful with ssl
- ssl_shutdown_timeout is only meaningful with ssl
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/755acd367bcab404.
Report an issue: GitHub.