encode/httpx · error · StreamClosed
Attempted to read or stream content, but the stream has been
Error message
Attempted to read or stream content, but the stream has been closed.
What it means
Raised as `StreamClosed` by sync `iter_raw()` when `is_closed` is True. Once a response stream is closed (explicitly via `.close()` or implicitly by being read to completion), further reads are rejected because the connection has been released back to the pool.
Source
Thrown at httpx/_models.py:942
yield chunk
def iter_lines(self) -> typing.Iterator[str]:
decoder = LineDecoder()
with request_context(request=self._request):
for text in self.iter_text():
for line in decoder.decode(text):
yield line
for line in decoder.flush():
yield line
def iter_raw(self, chunk_size: int | None = None) -> typing.Iterator[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, SyncByteStream):
raise RuntimeError("Attempted to call a sync iterator on an async stream.")
self.is_stream_consumed = True
self._num_bytes_downloaded = 0
chunker = ByteChunker(chunk_size=chunk_size)
with request_context(request=self._request):
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
self.close()
View on GitHub (pinned to b5addb64f0)
Solutions
- Perform all reads inside the `with client.stream(...) as r:` block.
- Call `response.read()` once inside the context and then use `response.content` outside it.
- If you need the body later, switch to a non-streaming `client.get()` which reads and keeps the body.
- Track a `closed` flag in your own code to avoid touching a closed response.
Example fix
// before
with client.stream('GET', url) as r:
pass
for c in r.iter_bytes(): # StreamClosed
...
// after
with client.stream('GET', url) as r:
r.read()
print(r.text) # buffered, safe outside the block Defensive patterns
Strategy: validation
Validate before calling
def is_open(resp: httpx.Response) -> bool:
return not resp.is_closed Type guard
import httpx
def stream_is_open(resp: httpx.Response) -> bool:
return not resp.is_closed Try / catch
try:
for chunk in response.iter_bytes():
...
except httpx.StreamClosed:
# re-issue the request for a fresh stream
response = client.get(response.request.url, stream=True) Prevention
- Consume the body fully inside the with client.stream(...) block.
- Call response.read() inside the block, use response.content outside.
- Track a closed flag in long-lived consumers.
When it happens
Trigger: Calling `response.iter_raw()` / `iter_bytes()` after `response.close()`, or after the `with client.stream(...)` context exited (which closes the response).
Common situations: Stepping outside a `with client.stream(...) as r:` block and then trying to read the body; calling `.read()` inside the block, exiting, then iterating; double close from a finally block.
Related errors
- Attempted to access streaming response content, without havi
- Attempted to read or stream some content, but the content ha
- The request instance has not been set on this response.
- Cannot call `raise_for_status` as the request instance has n
- Attempted to call a sync iterator on an async stream.
AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04).
Data as JSON: /data/errors/cfb4f23ef2b47af7.json.
Report an issue: GitHub.