aio-libs/aiohttp · error · RuntimeError

Called while some coroutine is waiting for incoming data.

Error message

Called while some coroutine is waiting for incoming data.

What it means

Raised by StreamReader.read_nowait when self._waiter exists and is not done, i.e. another coroutine is currently blocked in an async read (read/readline/readuntil) on the same reader. read_nowait is a synchronous drain of whatever is already buffered; calling it while a reader is suspended would steal its data and corrupt the read protocol.

Solutions

  1. Do not mix read_nowait with async reads on the same stream; pick one consumption style.
  2. Ensure no async read is in flight before calling read_nowait (check stream._waiter is None or done).
  3. On timeout, cancel the pending read task fully before draining.

Example fix

# before
asyncio.create_task(consume_async(stream))
rest = stream.read_nowait()  # raises

# after
# either consume fully async, or ensure no pending waiter first:
if stream._waiter is None or stream._waiter.done():
    rest = stream.read_nowait()
Defensive patterns

Strategy: validation

Validate before calling

w = stream._waiter
if w is not None and not w.done():
    raise RuntimeError('cannot read_nowait while async read pending')
rest = stream.read_nowait()

Type guard

def safe_to_read_nowait(stream) -> bool:
    w = getattr(stream, '_waiter', None)
    return w is None or w.done()

Try / catch

try:
    rest = stream.read_nowait()
except RuntimeError as e:
    if 'waiting for incoming data' in str(e):
        rest = b''
    else:
        raise

Prevention

When it happens

Trigger: Mixing await stream.read(n) (async) and stream.read_nowait() (sync) on the same reader concurrently; a cancellation/timeout leaving a _waiter pending and then calling read_nowait; a background task calling read_nowait while the main task reads.

Common situations: Timeout handlers that try to flush remaining data; custom protocols mixing sync and async reads; debugging code that dumps the buffer mid-read.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/46bcf1a0ad71e614. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/streams.py:532

            block = await self.read(n)
            if not block:
                partial = b"".join(blocks)
                raise asyncio.IncompleteReadError(partial, len(partial) + n)
            blocks.append(block)
            n -= len(block)

        return b"".join(blocks)

    def read_nowait(self, n: int = -1) -> bytes:
        # default was changed to be consistent with .read(-1)
        #
        # I believe the most users don't know about the method and
        # they are not affected.
        if self._exception is not None:
            raise self._exception

        if self._waiter and not self._waiter.done():
            raise RuntimeError(
                "Called while some coroutine is waiting for incoming data."
            )

        chunk = self._read_nowait(n)
        if chunk and (cb := self._on_chunk_received) is not None:
            # read_nowait is sync but the hook is async; schedule it so the
            # observability event still fires.
            # TODO: Save and await this task.
            asyncio.create_task(cb(chunk))  # type: ignore[unused-awaitable]
        return chunk

    def _read_nowait_chunk(self, n: int) -> bytes:
        first_buffer = self._buffer[0]
        offset = self._buffer_offset
        if n != -1 and len(first_buffer) - offset > n:
            data = first_buffer[offset : offset + n]
            self._buffer_offset += n

View on GitHub (pinned to d041d4d0fd)