python/cpython · error · ValueError

ssl_handshake_timeout is only meaningful with ssl

Error message

ssl_handshake_timeout is only meaningful with ssl

What it means

Raised by create_connection when ssl_handshake_timeout is given but ssl is not. The handshake timeout bounds how long the TLS handshake may take; it only exists in the TLS code path, so asyncio rejects it for plaintext connections as a likely configuration error.

Source

Thrown at Lib/asyncio/base_events.py:1106

        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

        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')

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Only pass ssl_handshake_timeout when ssl is truthy: include it in the same conditional branch as ssl.
  2. If plaintext is intended, remove the timeout argument.
  3. Group all TLS-only kwargs (ssl, server_hostname, ssl_handshake_timeout, ssl_shutdown_timeout) so they are set or omitted together.

Example fix

// before
await loop.create_connection(proto, h, p, ssl_handshake_timeout=10)  # no ssl

// after
if ctx is not None:
    t, p_ = await loop.create_connection(proto, h, p, ssl=ctx,
                                         ssl_handshake_timeout=10)
else:
    t, p_ = await loop.create_connection(proto, h, p)
Defensive patterns

Strategy: validation

Validate before calling

tls_kwargs = {}
if ctx is not None:
    tls_kwargs.update(ssl=ctx, ssl_handshake_timeout=10, ssl_shutdown_timeout=10)

Try / catch

try:
    await loop.create_connection(proto, h, p, ssl_handshake_timeout=10)
except ValueError:
    await loop.create_connection(proto, h, p, ssl=ssl.create_default_context(),
                                 ssl_handshake_timeout=10)

Prevention

When it happens

Trigger: Calling loop.create_connection(proto, host, port, ssl_handshake_timeout=10.0) with ssl unset. Typical when a TLS config object is spread into kwargs but its ssl key ended up None/omitted.

Common situations: Shared kwargs dicts for both TLS and plaintext clients; disabling TLS for local testing while leaving the timeout knob in place; refactors that made ssl conditional (**tls_kwargs) without cleaning the timeout.

Understand the failure class

Related errors


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