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 an async reader is currently blocked in _wait (self._waiter is pending). read_nowait is a synchronous drain that bypasses the waiter protocol; calling it concurrently with an awaited read would steal the awaited data. aiohttp refuses with RuntimeError to preserve the read-coroutine contract.

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 c0ef574e29)

Solutions

  1. Do not mix read_nowait with awaited reads on the same stream — pick one consumer model.
  2. Before read_nowait, check `if request.content._waiter and not request.content._waiter.done(): ...` (or just avoid read_nowait).
  3. Use readany() (async) instead of read_nowait for non-blocking-style reads.
  4. Ensure no background await is in flight when you call read_nowait.

Example fix

// before
# in a sync callback, while handler awaits request.read():
peek = request.content.read_nowait()  # RuntimeError
// after
# schedule the peek as async instead:
peek = await request.content.readany()
Defensive patterns

Strategy: validation

Validate before calling

def safe_read_nowait(stream, n=-1):
    waiter = getattr(stream, '_waiter', None)
    if waiter is not None and not waiter.done():
        raise RuntimeError('cannot read_nowait while async read is pending')
    return stream.read_nowait(n)

Try / catch

try:
    chunk = stream.read_nowait()
except RuntimeError as e:
    if 'waiting for incoming data' in str(e):
        # fall back to async read
        chunk = await stream.readany()
    else:
        raise

Prevention

When it happens

Trigger: Calling request.content.read_nowait() from sync code (e.g. a sync callback, __del__, signal handler) while an async task is awaiting request.content.read(...); mixing read_nowait with awaited reads for performance without coordination.

Common situations: Sync middleware/callback that peeks at the buffer while the handler is reading; instrumentation that drains for metrics; incorrect port of a sync API onto an async stream.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/46bcf1a0ad71e614.json. Report an issue: GitHub.