python/cpython · error · ValueError

You must set server_hostname when using ssl without a host

Error message

You must set server_hostname when using ssl without a host

What it means

Raised by create_connection when ssl is enabled but no host name is available to use as the TLS server_hostname. When server_hostname is omitted, asyncio defaults it to `host`; if host is empty/None (typical when connecting via a pre-made socket or only a port), certificate verification has nothing to verify against, so it refuses. Passing server_hostname='' explicitly bypasses the hostname check.

Source

Thrown at Lib/asyncio/base_events.py:1101

        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:
            raise ValueError(
                'ssl_handshake_timeout is only meaningful with ssl')

        if ssl_shutdown_timeout is not None and not ssl:
            raise ValueError(
                'ssl_shutdown_timeout is only meaningful with ssl')

        if sock is not None:
            _check_ssl_socket(sock)

        if happy_eyeballs_delay is not None and interleave is None:
            # If using happy eyeballs, default to interleave addresses by family
            interleave = 1

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass server_hostname explicitly: create_connection(proto, None, None, sock=s, ssl=ctx, server_hostname='example.com').
  2. If you deliberately want no hostname verification (rare, insecure), pass server_hostname=''.
  3. Otherwise supply host (and port) instead of a raw socket so asyncio can derive the name.

Example fix

// before
transport, proto = await loop.create_connection(
    factory, None, None, sock=raw_sock, ssl=ctx)  # ValueError

// after
transport, proto = await loop.create_connection(
    factory, None, None, sock=raw_sock, ssl=ctx,
    server_hostname='api.example.com')
Defensive patterns

Strategy: validation

Validate before calling

if ssl_ctx is not None and not host and not server_hostname:
    raise ConfigError('server_hostname required when using ssl without a host')

Try / catch

try:
    tp, pr = await loop.create_connection(proto, None, None, sock=s, ssl=ctx)
except ValueError as e:
    if 'server_hostname' not in str(e):
        raise
    tp, pr = await loop.create_connection(proto, None, None, sock=s, ssl=ctx,
                                          server_hostname=expected_name)

Prevention

When it happens

Trigger: Calling create_connection(proto, None, None, sock=s, ssl=ctx) — an already-connected socket with TLS but no server_hostname. Also create_connection(proto, '', 443, ssl=ctx).

Common situations: Wrapping an already-accepted/tunnelled socket (e.g. from a proxy CONNECT or a test harness) in TLS without naming the target; migrating code that used loop.sock_connect + manual SSL transport.

Understand the failure class

Related errors


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