{"id":"266a188feb523d88","repo":"encode/httpx","slug":"attempted-to-read-or-stream-some-content-but-the-266a18","errorCode":null,"errorMessage":"Attempted to read or stream some content, but the content has already been streamed. For requests, this could be due to passing a generator as request content, and then receiving a redirect response or a secondary request as part of an authentication flow.For responses, this could be due to attempting to stream the response content more than once.","messagePattern":"Attempted to read or stream some content, but the content has already been streamed\\. For requests, this could be due to passing a generator as request content, and then receiving a redirect response or a secondary request as part of an authentication flow\\.For responses, this could be due to attempting to stream the response content more than once\\.","errorType":"exception","errorClass":"StreamConsumed","httpStatus":null,"severity":"error","filePath":"httpx/_models.py","lineNumber":940,"sourceCode":"                yield chunk  # pragma: no cover\n            for chunk in chunker.flush():\n                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","sourceCodeStart":922,"sourceCodeEnd":958,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_models.py#L922-L958","documentation":"Raised as `StreamConsumed` by the sync `iter_raw()` method when `is_stream_consumed` is already True. A streaming body can be read exactly once; a second attempt is rejected because the underlying network bytes are gone. The message notes both request-side (generator content + redirect/auth retry) and response-side (double-streaming) causes.","triggerScenarios":"Iterating `response.iter_raw()` / `iter_bytes()` a second time; or on the request side, passing a generator as `content=` and letting httpx replay it after a redirect or during an auth challenge (the generator was already exhausted).","commonSituations":"Caching/memoizing a streaming response and then trying to iterate again; logging the body via `iter_bytes()` and then handing the same response to JSON parsing; using `yield`-based request bodies with redirects.","solutions":["Buffer the body once with `response.read()` and reuse `response.content` for subsequent access.","For request bodies that may be replayed (redirects/auth), pass bytes or a file object with `seek()`, not a generator.","Tee the stream into an in-memory buffer if you genuinely need multiple passes.","Re-issue the request if you need a fresh stream."],"exampleFix":"// before\nfor chunk in response.iter_bytes():\n    ...\nfor chunk in response.iter_bytes():  # StreamConsumed\n    ...\n\n// after\nresponse.read()\nbody = response.content  # reuse as often as needed","handlingStrategy":"validation","validationCode":"def can_iter(resp: httpx.Response) -> bool:\n    return not resp.is_stream_consumed and not resp.is_closed","typeGuard":"import httpx\n\ndef stream_is_fresh(resp: httpx.Response) -> bool:\n    return not (resp.is_stream_consumed or resp.is_closed)","tryCatchPattern":"try:\n    for chunk in response.iter_bytes():\n        ...\nexcept httpx.StreamConsumed:\n    response.read()  # fall back to buffered content if already consumed once\n    body = response.content","preventionTips":["Buffer once with .read() and reuse .content for multiple consumers.","Never pass a generator as request content if redirects/auth may replay it.","Tee into memory only when truly necessary."],"tags":["streaming","stream-consumed","response","request-body","runtime-error"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}