python/cpython · error · ExceptionGroup
staggered race failed
Error message
staggered race failed
What it means
Raised by the internal staggered race helper (Lib/asyncio/staggered.py) as an ExceptionGroup wrapping all sub-exceptions that the race's completion machinery could not attribute to a normal outcome. This helper powers asyncio.open_connection/start_connection's 'happy eyeballs' behavior of trying multiple addresses with delays, so in practice you see it when concurrent connection-attempt tasks die with unexpected exceptions. It is guarded by __debug__ because an unhandled exception here usually indicates a programming error or a broken loop callback rather than a normal network failure.
Source
Thrown at Lib/asyncio/staggered.py:166
futures.future_add_to_awaited_by(first_task, parent_task)
running_tasks.add(first_task)
first_task.add_done_callback(task_done)
# first_task has been appended to running_tasks before the event loop starts running it.
propagate_cancellation_error = None
# Make sure no tasks are left running if we leave this function
while running_tasks:
on_completed_fut = loop.create_future()
try:
await on_completed_fut
except exceptions_mod.CancelledError as ex:
propagate_cancellation_error = ex
for task in running_tasks:
task.cancel(*ex.args)
on_completed_fut = None
if __debug__ and unhandled_exceptions:
# If run_one_coro raises an unhandled exception, it's probably a
# programming error, and I want to see it.
raise ExceptionGroup("staggered race failed", unhandled_exceptions)
if propagate_cancellation_error is not None:
raise propagate_cancellation_error
return winner_result, winner_index, exceptions
finally:
del exceptions, propagate_cancellation_error, unhandled_exceptions, parent_task
View on GitHub (pinned to bc6749cc3b)
Solutions
- Read the wrapped exceptions: catch ExceptionGroup and inspect .exceptions to find the real cause before assuming a network problem
- If one address family consistently fails (e.g. broken IPv6), restrict attempts by passing a single resolved address or filtering getaddrinfo results
- Reproduce without python -O so the debug guard reports the group deterministically
- If the traceback points into asyncio internals, check your Python version against known asyncio bug fixes and upgrade
Example fix
// before
reader, writer = await asyncio.open_connection(host, port)
// after
try:
reader, writer = await asyncio.open_connection(host, port)
except BaseExceptionGroup as eg:
for sub in eg.exceptions:
print('attempt failed:', repr(sub))
raise Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try:
reader, writer = await asyncio.open_connection(host, port)
except* OSError as eg: # per-attempt network failures
log.debug('connect attempts failed: %r', eg.exceptions)
raise
except BaseExceptionGroup as eg: # staggered race programming errors
for sub in eg.exceptions:
log.exception('race task failed', exc_info=sub)
raise Prevention
- Pass explicit resolved addresses when one address family is known-broken instead of relying on happy-eyeballs races
- Run integration tests without -O so the __debug__ guard surfaces wrapped failures
- Never swallow an ExceptionGroup without inspecting .exceptions — the root cause is nested
When it happens
Trigger: Calling asyncio.open_connection()/start_connection() (or loop.start_connection()) with multiple resolved addresses where one of the spawned attempt tasks raises an exception not captured by the race's normal exception-recording path; any bug in a custom event loop, transport, or patched asyncio internals that makes the race's on_completed callback itself raise.
Common situations: DNS resolves a host to several addresses (IPv4+IPv6) and one attempt path raises an unexpected error; running under python -O changes whether the guard fires; monkey-patching of asyncio or a custom loop implementation interferes with task done-callbacks; rare upstream asyncio bugs during cancellation storms.
Related errors
- create_connection failed
- d3-flame-graph library failed to load
- no matching local address with {family=} found
- server_hostname is only meaningful with ssl
- host/port and sock can not be specified at the same time
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/04af6eef24ebdc22.
Report an issue: GitHub.