{"id":"cfb4f23ef2b47af7","repo":"encode/httpx","slug":"attempted-to-read-or-stream-content-but-the-strea-cfb4f2","errorCode":null,"errorMessage":"Attempted to read or stream content, but the stream has been closed.","messagePattern":"Attempted to read or stream content, but the stream has been closed\\.","errorType":"exception","errorClass":"StreamClosed","httpStatus":null,"severity":"error","filePath":"httpx/_models.py","lineNumber":942,"sourceCode":"                yield chunk\n\n    def iter_lines(self) -> typing.Iterator[str]:\n        decoder = LineDecoder()\n        with request_context(request=self._request):\n            for text in self.iter_text():\n                for line in decoder.decode(text):\n                    yield line\n            for line in decoder.flush():\n                yield line\n\n    def iter_raw(self, chunk_size: int | None = None) -> typing.Iterator[bytes]:\n        \"\"\"\n        A byte-iterator over the raw response content.\n        \"\"\"\n        if self.is_stream_consumed:\n            raise StreamConsumed()\n        if self.is_closed:\n            raise StreamClosed()\n        if not isinstance(self.stream, SyncByteStream):\n            raise RuntimeError(\"Attempted to call a sync iterator on an async stream.\")\n\n        self.is_stream_consumed = True\n        self._num_bytes_downloaded = 0\n        chunker = ByteChunker(chunk_size=chunk_size)\n\n        with request_context(request=self._request):\n            for raw_stream_bytes in self.stream:\n                self._num_bytes_downloaded += len(raw_stream_bytes)\n                for chunk in chunker.decode(raw_stream_bytes):\n                    yield chunk\n\n        for chunk in chunker.flush():\n            yield chunk\n\n        self.close()\n","sourceCodeStart":924,"sourceCodeEnd":960,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_models.py#L924-L960","documentation":"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.","triggerScenarios":"Calling `response.iter_raw()` / `iter_bytes()` after `response.close()`, or after the `with client.stream(...)` context exited (which closes the response).","commonSituations":"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.","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."],"exampleFix":"// before\nwith client.stream('GET', url) as r:\n    pass\nfor c in r.iter_bytes():  # StreamClosed\n    ...\n\n// after\nwith client.stream('GET', url) as r:\n    r.read()\nprint(r.text)  # buffered, safe outside the block","handlingStrategy":"validation","validationCode":"def is_open(resp: httpx.Response) -> bool:\n    return not resp.is_closed","typeGuard":"import httpx\n\ndef stream_is_open(resp: httpx.Response) -> bool:\n    return not resp.is_closed","tryCatchPattern":"try:\n    for chunk in response.iter_bytes():\n        ...\nexcept httpx.StreamClosed:\n    # re-issue the request for a fresh stream\n    response = client.get(response.request.url, stream=True)","preventionTips":["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."],"tags":["streaming","stream-closed","response","context-manager","runtime-error"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}