{"record":{"id":"04af6eef24ebdc22","repo":"python/cpython","slug":"staggered-race-failed","errorCode":null,"errorMessage":"staggered race failed","messagePattern":"staggered race failed","errorType":"exception","errorClass":"ExceptionGroup","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/staggered.py","lineNumber":166,"sourceCode":"        futures.future_add_to_awaited_by(first_task, parent_task)\n        running_tasks.add(first_task)\n        first_task.add_done_callback(task_done)\n        # first_task has been appended to running_tasks before the event loop starts running it.\n        propagate_cancellation_error = None\n        # Make sure no tasks are left running if we leave this function\n        while running_tasks:\n            on_completed_fut = loop.create_future()\n            try:\n                await on_completed_fut\n            except exceptions_mod.CancelledError as ex:\n                propagate_cancellation_error = ex\n                for task in running_tasks:\n                    task.cancel(*ex.args)\n            on_completed_fut = None\n        if __debug__ and unhandled_exceptions:\n            # If run_one_coro raises an unhandled exception, it's probably a\n            # programming error, and I want to see it.\n            raise ExceptionGroup(\"staggered race failed\", unhandled_exceptions)\n        if propagate_cancellation_error is not None:\n            raise propagate_cancellation_error\n        return winner_result, winner_index, exceptions\n    finally:\n        del exceptions, propagate_cancellation_error, unhandled_exceptions, parent_task\n","sourceCodeStart":148,"sourceCodeEnd":172,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/staggered.py#L148-L172","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nreader, writer = await asyncio.open_connection(host, port)\n\n// after\ntry:\n    reader, writer = await asyncio.open_connection(host, port)\nexcept BaseExceptionGroup as eg:\n    for sub in eg.exceptions:\n        print('attempt failed:', repr(sub))\n    raise","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try:\n    reader, writer = await asyncio.open_connection(host, port)\nexcept* OSError as eg:      # per-attempt network failures\n    log.debug('connect attempts failed: %r', eg.exceptions)\n    raise\nexcept BaseExceptionGroup as eg:  # staggered race programming errors\n    for sub in eg.exceptions:\n        log.exception('race task failed', exc_info=sub)\n    raise","preventionTips":["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"],"tags":["asyncio","network","concurrency","exception-group"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}