encode/httpx · warning · RequestNotRead

Attempted to access streaming request content, without havin

Error message

Attempted to access streaming request content, without having called `read()`.

What it means

This is httpx.RequestNotRead raised by the Request.content property when _content has not been materialized yet. Unlike Response.content (which triggers a read), Request.content requires you to have explicitly called request.read() (or aread()) first, because a streaming request body cannot be auto-read without side effects on the underlying generator.

Source

Thrown at httpx/_models.py:465

        auto_headers: list[tuple[bytes, bytes]] = []

        has_host = "Host" in self.headers
        has_content_length = (
            "Content-Length" in self.headers or "Transfer-Encoding" in self.headers
        )

        if not has_host and self.url.host:
            auto_headers.append((b"Host", self.url.netloc))
        if not has_content_length and self.method in ("POST", "PUT", "PATCH"):
            auto_headers.append((b"Content-Length", b"0"))

        self.headers = Headers(auto_headers + self.headers.raw)

    @property
    def content(self) -> bytes:
        if not hasattr(self, "_content"):
            raise RequestNotRead()
        return self._content

    def read(self) -> bytes:
        """
        Read and return the request content.
        """
        if not hasattr(self, "_content"):
            assert isinstance(self.stream, typing.Iterable)
            self._content = b"".join(self.stream)
            if not isinstance(self.stream, ByteStream):
                # If a streaming request has been read entirely into memory, then
                # we can replace the stream with a raw bytes implementation,
                # to ensure that any non-replayable streams can still be used.
                self.stream = ByteStream(self._content)
        return self._content

    async def aread(self) -> bytes:
        """

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Call request.read() (sync) or await request.aread() (async) before accessing .content.
  2. Build the Request with content=<bytes> instead of a generator so .content is immediately available.
  3. Use request.stream only when you genuinely want streaming, and read() before inspection.
  4. In middleware, guard: if not hasattr(request, '_content'): request.read().

Example fix

// before
req = httpx.Request('POST', url, content=gen)
print(req.content)  # RequestNotRead
// after
req = httpx.Request('POST', url, content=gen)
req.read()
print(req.content)
# or simpler:
req = httpx.Request('POST', url, content=b'...bytes...')
print(req.content)
Defensive patterns

Strategy: validation

Validate before calling

# Ensure the request body is materialized before accessing .content
if not hasattr(request, '_content'):
    request.read()  # sync; use await request.aread() for async
body = request.content

Type guard

def request_content_ready(request: 'httpx.Request') -> bool:
    return hasattr(request, '_content')

Try / catch

try:
    body = request.content
except httpx.RequestNotRead:
    request.read()
    body = request.content

Prevention

When it happens

Trigger: Calling request.content on a freshly built Request whose body is still a stream/generator and you have not called read(); inspecting a Request that was sent with content=<generator> before reading; mocking a Request without materializing its content.

Common situations: Logging/inspection middleware that reads request.content before the request is sent; test assertions on a constructed-but-unsent Request; building a Request manually with stream=<generator> and accessing .content.

Related errors


AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04). Data as JSON: /data/errors/9f0a568f8f9273df.json. Report an issue: GitHub.