encode/httpx · error · RuntimeError
Attempted to call an async iterator on a sync stream.
Error message
Attempted to call an async iterator on a sync stream.
What it means
Raised as `RuntimeError` by async `aiter_raw()` when `self.stream` is not an `AsyncByteStream`. The Response came from a sync `httpx.Client` but you used an async iteration method. httpx refuses to silently run a sync iterator inside an async context.
Source
Thrown at httpx/_models.py:1048
with request_context(request=self._request):
async for text in self.aiter_text():
for line in decoder.decode(text):
yield line
for line in decoder.flush():
yield line
async def aiter_raw(
self, chunk_size: int | None = None
) -> typing.AsyncIterator[bytes]:
"""
A byte-iterator over the raw response content.
"""
if self.is_stream_consumed:
raise StreamConsumed()
if self.is_closed:
raise StreamClosed()
if not isinstance(self.stream, AsyncByteStream):
raise RuntimeError("Attempted to call an async iterator on a sync stream.")
self.is_stream_consumed = True
self._num_bytes_downloaded = 0
chunker = ByteChunker(chunk_size=chunk_size)
with request_context(request=self._request):
async for raw_stream_bytes in self.stream:
self._num_bytes_downloaded += len(raw_stream_bytes)
for chunk in chunker.decode(raw_stream_bytes):
yield chunk
for chunk in chunker.flush():
yield chunk
await self.aclose()
async def aclose(self) -> None:
"""View on GitHub (pinned to b5addb64f0)
Solutions
- Use sync iteration (`for chunk in response.iter_bytes()`, `response.read()`) for sync-client responses.
- Pick one concurrency model per code path and stick to it.
- If async is required, switch the client to `httpx.AsyncClient`.
- Type-annotate helpers so mypy/pyright flags the mismatch.
Example fix
// before
async def handler():
r = sync_client.get(url)
async for c in r.aiter_bytes(): # RuntimeError
...
// after
async def handler():
async with httpx.AsyncClient() as c:
r = await c.get(url)
async for c in r.aiter_bytes():
... Defensive patterns
Strategy: type-guard
Validate before calling
from httpx._transports.default import SyncByteStream
def is_sync_response(resp: httpx.Response) -> bool:
return isinstance(resp.stream, SyncByteStream) Type guard
import httpx
from httpx._transports.default import SyncByteStream
def is_sync_stream(resp: httpx.Response) -> bool:
return isinstance(resp.stream, SyncByteStream) Try / catch
try:
async for chunk in response.aiter_bytes():
...
except RuntimeError:
# sync stream - use sync iteration
for chunk in response.iter_bytes():
... Prevention
- Only call async methods on responses from AsyncClient.
- Keep sync and async code paths strictly separated.
- If async is required, migrate the client to AsyncClient.
When it happens
Trigger: Calling `async for chunk in response.aiter_bytes():` or `await response.aread()` on a Response returned by sync `httpx.Client.get()`.
Common situations: Accidentally wrapping sync client calls in an async function; shared helpers originally written for async that get reused with a sync client; framework code that assumes async responses everywhere.
Related errors
- Attempted to call a sync iterator on an async stream.
- Attempted to call a sync close on an async stream.
- Attempted to call an async close on a sync stream.
- Attempted to send an async request with a sync Client instan
- Attempted to send a sync request with an AsyncClient instanc
AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04).
Data as JSON: /data/errors/04dc5e96abc5dfa1.json.
Report an issue: GitHub.