{"record":{"id":"ecaf8b5b8bcaaaf9","repo":"aio-libs/aiohttp","slug":"s-called-while-another-coroutine-is-already-wai","errorCode":null,"errorMessage":"%s() called while another coroutine is already waiting for incoming data","messagePattern":"(.+?)\\(\\) called while another coroutine is already waiting for incoming data","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/streams.py","lineNumber":358,"sourceCode":"        if len(self._http_chunk_splits) > self._high_water_chunks:\n            self._protocol.pause_reading()\n\n        # wake up readchunk when end of http chunk received\n        waiter = self._waiter\n        if waiter is not None:\n            self._waiter = None\n            set_result(waiter, None)\n\n    async def _wait(self, func_name: str) -> None:\n        if not self._protocol.connected:\n            raise RuntimeError(\"Connection closed.\")\n\n        # StreamReader uses a future to link the protocol feed_data() method\n        # to a read coroutine. Running two read coroutines at the same time\n        # would have an unexpected behaviour. It would not possible to know\n        # which coroutine would get the next data.\n        if self._waiter is not None:\n            raise RuntimeError(\n                \"%s() called while another coroutine is \"\n                \"already waiting for incoming data\" % func_name\n            )\n\n        waiter = self._waiter = self._loop.create_future()\n        try:\n            with self._timer:\n                await waiter\n        finally:\n            self._waiter = None\n\n    async def _fire_chunk_received(self, chunk: bytes) -> None:\n        cb = self._on_chunk_received\n        assert cb is not None\n        # Run under the same per-stream timer that _wait() uses, so a hung\n        # trace handler is bounded by sock_read just like a hung socket read would be.\n        with self._timer:\n            await cb(chunk)","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/streams.py#L340-L376","documentation":"Raised by StreamReader._wait when self._waiter is already an unresolved Future, meaning another coroutine is currently awaiting incoming data on the same reader. StreamReader links the protocol's feed_data to a single read coroutine via one waiter future; two concurrent readers would race unpredictably for each chunk, so aiohttp forbids it.","triggerScenarios":"Two tasks both calling await content.read(n) (or readline/readchunk) on the same response/request stream at the same time; a background task draining the body while the main task also reads; calling read while a previous read is still suspended.","commonSituations":"Spawning asyncio.gather over the same stream; a progress-monitoring task reading the body alongside the consumer; reusing a single response across multiple worker tasks.","solutions":["Serialize all reads of one stream through a single consumer task; do not share a StreamReader between concurrent coroutines.","If you need fan-out, have one reader push chunks into an asyncio.Queue and have workers read from the queue.","Read the whole body once (await response.read()) and then distribute the bytes."],"exampleFix":"# before\nasync def worker(c):\n    return await c.read(100)\nawait asyncio.gather(worker(content), worker(content))\n\n# after\nbody = await response.read()  # single consumer\n# split body between workers as needed","handlingStrategy":"validation","validationCode":"if stream._waiter is not None and not stream._waiter.done():\n    raise RuntimeError('another reader is active on this stream')\ndata = await stream.read(n)","typeGuard":"def stream_idle(stream) -> bool:\n    w = getattr(stream, '_waiter', None)\n    return w is None or w.done()","tryCatchPattern":"try:\n    data = await stream.read(n)\nexcept RuntimeError as e:\n    if 'already waiting' in str(e):\n        # serialize reads instead of concurrent\n        raise\n    raise","preventionTips":["Never share a single StreamReader across concurrent tasks.","Read each stream through exactly one consumer coroutine.","Fan out via asyncio.Queue fed by the single reader."],"tags":["streams","concurrency","async","api-misuse"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}