psf/requests · error · RuntimeError

The content for this response was already consumed

Error message

The content for this response was already consumed

What it means

A `RuntimeError` raised by the `Response.content` property when the response body was already consumed from the underlying stream (`_content_consumed` is True) but never cached into `_content` (still False). This happens only for streamed responses: once you iterate the raw stream, the bytes are gone from the socket, so a later `.content`/`.text`/`.json()` access cannot recover them.

Source

Thrown at src/requests/models.py:1041

            if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
                pending = lines.pop()
            else:
                pending = None

            yield from lines

        if pending is not None:
            yield pending

    @property
    def content(self) -> bytes:
        """Content of the response, in bytes."""

        if self._content is False:
            # Read the contents.
            if self._content_consumed:
                raise RuntimeError("The content for this response was already consumed")

            if self.status_code == 0 or self.raw is None:
                self._content = None
            else:
                self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b""

        self._content_consumed = True
        # don't need to release the connection; that's been handled by urllib3
        # since we exhausted the data.
        return self._content  # type: ignore[return-value]

    @property
    def text(self) -> str:
        """Content of the response, in unicode.

        If Response.encoding is None, encoding will be guessed using
        ``charset_normalizer`` or ``chardet``.

View on GitHub ↗ (pinned to 414f0513c3)

Solutions

  1. Access `resp.content` (or `.text`/`.json()`) FIRST — it caches the whole body, and subsequent `iter_content` calls replay from the cache.
  2. If you need both streaming and later access, tee the chunks yourself: collect them into a buffer while streaming and reuse that buffer.
  3. If streaming isn't actually needed, drop `stream=True` so the body is read and cached eagerly.
  4. Don't reuse a drained streamed Response across consumers — re-request or pass the saved bytes instead.

Example fix

# before
resp = requests.get(url, stream=True)
for chunk in resp.iter_content(8192):
    f.write(chunk)
log.debug(resp.text)  # RuntimeError

# after
resp = requests.get(url, stream=True)
buf = bytearray()
for chunk in resp.iter_content(8192):
    f.write(chunk)
    buf.extend(chunk)
log.debug(buf.decode(resp.encoding or 'utf-8'))
Defensive patterns

Strategy: try-catch

Validate before calling

# safe to touch .content only if already consumed or stream still open
can_read = resp._content is not False or not resp.raw.closed  # or simply: access resp.content once and reuse it

Try / catch

try:
    body = resp.content
except RuntimeError:
    # stream was partially read via iter_content/raw then closed
    ...  # re-issue the request; the body is unrecoverable

Prevention

When it happens

Trigger: With `requests.get(url, stream=True)`: first drain the body via `iter_content()`/`iter_lines()` (or `raw.read`), then access `resp.content`, `resp.text`, or `resp.json()`. Accessing `.content` first and iterating later does NOT raise — caching only fails in the stream-then-access order.

Common situations: Logging middleware or debugging code that calls `resp.text` after the application already streamed the body to disk; retry/error handlers that try to read `resp.json()` from a response that was partially streamed; sharing a streamed Response object between two consumers.

Related errors


AI-assisted analysis of psf/requests@414f0513c3 (2026-07-31). Data as JSON: /data/errors/d7997d5baf4be9bb.json. Report an issue: GitHub ↗.