python/cpython · error · TypeError
ssl argument must be an SSLContext or None
Error message
ssl argument must be an SSLContext or None
What it means
Raised by create_server() when the ssl argument is a bool (True/False). Historically ssl=True was interpreted as a default context; that footgun was removed, so ssl must be an ssl.SSLContext or None — True does not mean 'default context' anymore.
Source
Thrown at Lib/asyncio/base_events.py:1572
ssl_shutdown_timeout=None,
start_serving=True):
"""Create a TCP server.
The host parameter can be a string, in that case the TCP server is
bound to host and port.
The host parameter can also be a sequence of strings and in that
case the TCP server is bound to all hosts of the sequence. If
a host appears multiple times (possibly indirectly e.g. when
hostnames resolve to the same IP address), the server is only bound
once to that host.
Return a Server object which can be used to stop the service.
This method is a coroutine.
"""
if isinstance(ssl, bool):
raise TypeError('ssl argument must be an SSLContext or None')
if ssl_handshake_timeout is not None and ssl is None:
raise ValueError(
'ssl_handshake_timeout is only meaningful with ssl')
if ssl_shutdown_timeout is not None and ssl is None:
raise ValueError(
'ssl_shutdown_timeout is only meaningful with ssl')
if sock is not None:
_check_ssl_socket(sock)
if host is not None or port is not None:
if sock is not None:
raise ValueError(
'host/port and sock can not be specified at the same time')
if reuse_address is None:View on GitHub (pinned to bc6749cc3b)
Solutions
- Pass ssl=ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) (plus load_cert_chain) for a TLS server
- Pass ssl=None (or omit it) for plaintext
- If a bool flag drives TLS in your config, branch on it and select the context yourself
Example fix
# before
server = await loop.create_server(factory, '0.0.0.0', 443, ssl=True)
# after
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.load_cert_chain('cert.pem', 'key.pem')
server = await loop.create_server(factory, '0.0.0.0', 443, ssl=ctx) Defensive patterns
Strategy: type-guard
Validate before calling
import ssl assert ssl is None or isinstance(ssl, ssl.SSLContext), 'ssl must be SSLContext or None'
Type guard
import ssl
def is_server_ssl(v: object) -> TypeGuard[ssl.SSLContext | None]:
return v is None or isinstance(v, ssl.SSLContext) Prevention
- Never pass booleans as the ssl argument; build contexts explicitly
- Name the config field tls_context, not ssl_enabled, so a bool cannot leak in
When it happens
Trigger: loop.create_server(factory, host, port, ssl=True) or ssl=False; any code passing a boolean flag copied from an old tutorial.
Common situations: Pre-3.11-era code or copy-pasted examples using ssl=True; wrapping the ssl parameter in a feature flag (ssl=use_tls) where use_tls is a bool.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Neither host/port nor sock were specified
- Server side SSL needs a valid SSLContext
- Node can't be used without a value attribute.
- Socket cannot be of type SSLSocket
- server_hostname is only meaningful with ssl
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/262a1668f5ad1bfd.
Report an issue: GitHub.