{"id":959,"hash":"55f4756716ff8b96bd08e065836e11ab712eb6f8374b809f4147430266de4b92","pattern":"Asynchronous exception handling in Python","full_message":"I've the following code using asyncio and aiohttp to make asynchronous HTTP requests.\n\nimport sys\nimport asyncio\nimport aiohttp\n\n@asyncio.coroutine\ndef get(url):\n    try:\n        print('GET %s' % url)\n        resp = yield from aiohttp.request('GET', url)\n    except Exception as e:\n        raise Exception(\"%s has error '%s'\" % (url, e))\n    else:\n        if resp.status >= 400:\n            raise Exception(\"%s has error '%s: %s'\" % (url, resp.status, resp.reason))\n\n    return (yield from resp.text())\n\n@asyncio.coroutine\ndef fill_data(run):\n    url = 'http://www.google.com/%s' % run['name']\n    run['data'] = yield from get(url)\n\ndef get_runs():\n    runs = [ {'name': 'one'}, {'name': 'two'} ]\n    loop = asyncio.get_event_loop()\n    task = asyncio.wait([fill_data(r) for r in runs])\n    loop.run_until_complete(task)   \n    return runs\n\ntry:\n    get_runs()\nexcept Exception as e:\n    print(repr(e))\n    sys.exit(1)\n\nFor some reason, exceptions raised inside the get function are not caught:\n\nFuture/Task exception was never retrieved\nTraceback (most recent call last):\n  File \"site-packages/asyncio/tasks.py\", line 236, in _step\n    result = coro.send(value)\n  File \"mwe.py\", line 25, in fill_data\n    run['data'] = yield from get(url)\n  File \"mwe.py\", line 17, in get\n    raise Exception(\"%s has error '%s: %s'\" % (url, resp.status, resp.reason))\nException: http://www.google.com/two has error '404: Not Found'\n\nSo, what is correct way to handle exceptions raised by coroutines?","ecosystem":"pypi","package_name":"python-3.x","package_version":null,"solution":"asyncio.wait doesn't actually consume the Futures passed to it, it just waits for them to complete, and then returns the Future objects:\n\ncoroutine asyncio.wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED)\n\nWait for the Futures and coroutine objects\ngiven by the sequence futures to complete. Coroutines will be wrapped\nin Tasks. Returns two sets of Future: (done, pending).\n\nUntil you actually yield from/await the items in the done list, they'll remain unconsumed. Since your program exits without consuming the futures, you see the \"exception was never retrieved\" messages.\n\nFor your use-case, it probably makes more sense to use asyncio.gather, which will actually consume each Future, and then return a single Future that aggregates all their results (or raises the first Exception thrown by a future in the input list).\n\ndef get_runs():\n    runs = [ {'name': 'one'}, {'name': 'two'} ]\n    loop = asyncio.get_event_loop()\n    tasks = asyncio.gather(*[fill_data(r) for r in runs])\n    loop.run_until_complete(tasks)\n    return runs\n\nOutput:\n\nGET http://www.google.com/two\nGET http://www.google.com/one\nException(\"http://www.google.com/one has error '404: Not Found'\",)\n\nNote that asyncio.gather actually lets you customize its behavior when one of the futures raises an exception; the default behavior is to raise the first exception it hits, but it can also just return each exception object in the output list:\n\nasyncio.gather(*coros_or_futures, loop=None, return_exceptions=False)\n\nReturn a future aggregating results from the given coroutine objects\nor futures.\n\nAll futures must share the same event loop. If all the tasks are done\nsuccessfully, the returned future’s result is the list of results (in\nthe order of the original sequence, not necessarily the order of\nresults arrival). If return_exceptions is True, exceptions in the\ntasks are treated the same as successful results, and gathered in the\nresult list; otherwise, the first raised exception will be immediately\npropagated to the returned future.","confidence":0.95,"source":"stackoverflow","source_url":"https://stackoverflow.com/questions/30361824/asynchronous-exception-handling-in-python","votes":84,"created_at":"2026-04-19T04:52:05.809749+00:00","updated_at":"2026-04-19T04:52:05.809749+00:00"}