python/cpython · error · ValueError

server_hostname is only meaningful with ssl

Error message

server_hostname is only meaningful with ssl

What it means

Raised by BaseEventLoop.create_connection when the caller passes server_hostname together with ssl=None/False. server_hostname is only used to verify the TLS peer's certificate; without an ssl context/protocol there is no certificate check, so the argument is meaningless and asyncio rejects it to catch configuration mistakes.

Source

Thrown at Lib/asyncio/base_events.py:1087

            proto=0, flags=0, sock=None,
            local_addr=None, server_hostname=None,
            ssl_handshake_timeout=None,
            ssl_shutdown_timeout=None,
            happy_eyeballs_delay=None, interleave=None,
            all_errors=False):
        """Connect to a TCP server.

        Create a streaming transport connection to a given internet host and
        port: socket family AF_INET or socket.AF_INET6 depending on host (or
        family if specified), socket type SOCK_STREAM. protocol_factory must
        be a callable returning a protocol instance.

        This method is a coroutine which will try to establish the
        connection in the background.  When successful, the coroutine
        returns a (transport, protocol) pair.
        """
        if server_hostname is not None and not ssl:
            raise ValueError('server_hostname is only meaningful with ssl')

        if server_hostname is None and ssl:
            # Use host as default for server_hostname.  It is an error
            # if host is empty or not set, e.g. when an
            # already-connected socket was passed or when only a port
            # is given.  To avoid this error, you can pass
            # server_hostname='' -- this will bypass the hostname
            # check.  (This also means that if host is a numeric
            # IP/IPv6 address, we will attempt to verify that exact
            # address; this will probably fail, but it is possible to
            # create a certificate for a specific IP address, so we
            # don't judge it here.)
            if not host:
                raise ValueError('You must set server_hostname '
                                 'when using ssl without a host')
            server_hostname = host

        if ssl_handshake_timeout is not None and not ssl:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass an ssl context: ssl=ssl.create_default_context() alongside server_hostname.
  2. If the connection is intentionally plaintext, drop server_hostname from the call.
  3. Build kwargs conditionally: kwargs['server_hostname'] = name only when ssl is set.

Example fix

// before
await loop.create_connection(proto, host, port, server_hostname='example.com')  # no ssl

// after
kwargs = {}
if use_tls:
    kwargs['ssl'] = ssl.create_default_context()
    kwargs['server_hostname'] = 'example.com'
await loop.create_connection(proto, host, port, **kwargs)
Defensive patterns

Strategy: validation

Validate before calling

if server_hostname is not None and not ssl_ctx:
    raise ConfigError('server_hostname requires an ssl context')

Try / catch

try:
    await loop.create_connection(proto, host, port, server_hostname=name)
except ValueError as e:
    if 'only meaningful with ssl' not in str(e):
        raise
    await loop.create_connection(proto, host, port,
                                 ssl=ssl.create_default_context(),
                                 server_hostname=name)

Prevention

When it happens

Trigger: Calling loop.create_connection(factory, host, port, server_hostname='example.com') with no ssl= argument (ssl defaults to None). Common when TLS support was removed or made conditional but the hostname argument stayed.

Common situations: Feature-flagged TLS: code built the server_hostname string eagerly but passed ssl=None in the plaintext branch; refactors that dropped the ssl argument; testing against a local plaintext server with leftover TLS kwargs.

Understand the failure class

Related errors


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