python/cpython · error · ValueError
ssl_shutdown_timeout should be a positive number, got {ssl_s
Error message
ssl_shutdown_timeout should be a positive number, got {ssl_shutdown_timeout} What it means
Raised in _SSLProtocol.__init__ as ValueError when an explicitly supplied ssl_shutdown_timeout is <= 0 (and not None). This timeout bounds the TLS shutdown/close_notify exchange when the connection is torn down; non-positive values are rejected because the graceful-shutdown wait would be degenerate.
Source
Thrown at Lib/asyncio/sslproto.py:290
call_connection_made=True,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None):
if ssl is None:
raise RuntimeError("stdlib ssl module not available")
self._ssl_buffer = bytearray(self.max_size)
self._ssl_buffer_view = memoryview(self._ssl_buffer)
if ssl_handshake_timeout is None:
ssl_handshake_timeout = constants.SSL_HANDSHAKE_TIMEOUT
elif ssl_handshake_timeout <= 0:
raise ValueError(
f"ssl_handshake_timeout should be a positive number, "
f"got {ssl_handshake_timeout}")
if ssl_shutdown_timeout is None:
ssl_shutdown_timeout = constants.SSL_SHUTDOWN_TIMEOUT
elif ssl_shutdown_timeout <= 0:
raise ValueError(
f"ssl_shutdown_timeout should be a positive number, "
f"got {ssl_shutdown_timeout}")
if not sslcontext:
sslcontext = _create_transport_context(
server_side, server_hostname)
self._server_side = server_side
if server_hostname and not server_side:
self._server_hostname = server_hostname
else:
self._server_hostname = None
self._sslcontext = sslcontext
# SSL-specific extra info. More info are set when the handshake
# completes.
self._extra = dict(sslcontext=sslcontext)
# App data write bufferingView on GitHub (pinned to bc6749cc3b)
Solutions
- Use a positive duration: ssl_shutdown_timeout=5.
- Use None/omit the parameter for the default (constants.SSL_SHUTDOWN_TIMEOUT, 30s).
- Sanitize numeric config inputs at load time, rejecting non-positive timeout values early with a clear message.
Example fix
// before await loop.create_connection(proto, 'h', 443, ssl=ctx, ssl_shutdown_timeout=0) # ValueError // after await loop.create_connection(proto, 'h', 443, ssl=ctx, ssl_shutdown_timeout=5.0)
Defensive patterns
Strategy: validation
Validate before calling
def shutdown_timeout(value):
if value is None:
return None # asyncio default (30s)
value = float(value)
if value <= 0:
raise ValueError('ssl_shutdown_timeout must be > 0')
return value
await loop.create_connection(proto, 'h', 443, ssl=ctx,
ssl_shutdown_timeout=shutdown_timeout(cfg)) Type guard
def is_positive_timeout(v) -> bool:
return v is None or (isinstance(v, (int, float)) and not isinstance(v, bool) and v > 0) Try / catch
try:
await loop.create_connection(proto, 'h', 443, ssl=ctx, ssl_shutdown_timeout=t)
except ValueError as e:
if 'ssl_shutdown_timeout' in str(e):
await loop.create_connection(proto, 'h', 443, ssl=ctx)
else:
raise Prevention
- Use None or a positive seconds value for ssl_shutdown_timeout.
- Share one timeout validator for handshake and shutdown kwargs.
- Reject non-positive values in config parsing with explicit errors.
When it happens
Trigger: Passing ssl_shutdown_timeout=0 or a negative value to the asyncio SSL entry points (loop.create_connection, create_server, start_server, start_tls) which forward it into _SSLProtocol.
Common situations: Mirroring ssl_handshake_timeout tuning and setting both to 0 intending 'no timeout'; env-driven config parsing empty values as 0; CI configs that clamp all timeouts to 0 for speed.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ssl_handshake_timeout should be a positive number, got {ssl_
- ssl_handshake_timeout is only meaningful with ssl
- ssl_shutdown_timeout is only meaningful with ssl
- high (%r) must be >= low (%r) must be >= 0
- Socket cannot be of type SSLSocket
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/650b72fb0ef3b78f.
Report an issue: GitHub.