encode/httpx · error · RuntimeError

Cannot send a request, as the client has been closed.

Error message

Cannot send a request, as the client has been closed.

What it means

Raised as RuntimeError by the sync Client.send() path when self._state == ClientState.CLOSED. Once close()/__exit__ has run the client refuses any further requests.

Source

Thrown at httpx/_client.py:901

        stream: bool = False,
        auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT,
        follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
    ) -> Response:
        """
        Send a request.

        The request is sent as-is, unmodified.

        Typically you'll want to build one with `Client.build_request()`
        so that any client-level configuration is merged into the request,
        but passing an explicit `httpx.Request()` is supported as well.

        See also: [Request instances][0]

        [0]: /advanced/clients/#request-instances
        """
        if self._state == ClientState.CLOSED:
            raise RuntimeError("Cannot send a request, as the client has been closed.")

        self._state = ClientState.OPENED
        follow_redirects = (
            self.follow_redirects
            if isinstance(follow_redirects, UseClientDefault)
            else follow_redirects
        )

        self._set_timeout(request)

        auth = self._build_request_auth(request, auth)

        response = self._send_handling_auth(
            request,
            auth=auth,
            follow_redirects=follow_redirects,
            history=[],
        )

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Perform all requests inside a single 'with httpx.Client() as client:' block.
  2. If the client was closed, construct a fresh httpx.Client instead of reusing it.
  3. Avoid calling client.close() (or exiting its context) before outstanding requests finish.

Example fix

// before
with httpx.Client() as c:
    pass
c.get(url)  # RuntimeError: closed
// after
with httpx.Client() as c:
    c.get(url)
Defensive patterns

Strategy: validation

Validate before calling

import httpx
def ensure_open(client: httpx.Client) -> None:
    if client.is_closed:
        raise RuntimeError("client is closed; create a new httpx.Client")

ensure_open(client)
client.get(url)

Type guard

import httpx

def is_usable(client: httpx.Client) -> bool:
    return not client.is_closed

Try / catch

try:
    client.get(url)
except RuntimeError as exc:
    if "has been closed" in str(exc):
        client = httpx.Client(...)  # recreate
        client.get(url)
    else:
        raise

Prevention

When it happens

Trigger: Calling client.get/send/client.stream after client.close(), or after the 'with client:' block has exited (which sets state to CLOSED).

Common situations: Reusing a client stored globally after it was closed; issuing requests outside the with-block; a shared client closed by one consumer while another still uses it.

Related errors


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