encode/httpx · error · HTTPStatusError

{error_type} '{0.status_code} {0.reason_phrase}' for url '{0

Error message

{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}

What it means

The non-redirect message format used by `raise_for_status` for any non-success response without a redirect location (4xx, 5xx, or other). It raises `HTTPStatusError` with status code, reason phrase, URL, and an MDN docs link. The `error_type` prefix is chosen from the status class ('Client error', 'Server error', 'Redirect response', 'Informational response').

Source

Thrown at httpx/_models.py:829

                "Redirect location: '{0.headers[location]}'\n"
                "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}"
            )
        else:
            message = (
                "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n"
                "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}"
            )

        status_class = self.status_code // 100
        error_types = {
            1: "Informational response",
            3: "Redirect response",
            4: "Client error",
            5: "Server error",
        }
        error_type = error_types.get(status_class, "Invalid status code")
        message = message.format(self, error_type=error_type)
        raise HTTPStatusError(message, request=request, response=self)

    def json(self, **kwargs: typing.Any) -> typing.Any:
        return jsonlib.loads(self.content, **kwargs)

    @property
    def cookies(self) -> Cookies:
        if not hasattr(self, "_cookies"):
            self._cookies = Cookies()
            self._cookies.extract_cookies(self)
        return self._cookies

    @property
    def links(self) -> dict[str | None, dict[str, str]]:
        """
        Returns the parsed header links of the response, if any
        """
        header = self.headers.get("link")
        if header is None:

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Wrap the call: `try: r.raise_for_status() except httpx.HTTPStatusError as e: ...` and branch on `e.response.status_code`.
  2. Add retries with backoff for 5xx (and 429) only — do NOT blind-retry 4xx.
  3. For auth flows, refresh the token on 401 before retrying once.
  4. Log `e.response.text` / `e.response.json()` for diagnostics but avoid leaking it to end users.

Example fix

// before
r = client.get(url)
r.raise_for_status()  # raises on 404, unhandled

// after
try:
    r = client.get(url)
    r.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 404:
        return None
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

if not response.is_success:
    status = response.status_code
    # branch on 4xx vs 5xx before raise_for_status()

Type guard

import httpx

def is_http_error(resp: httpx.Response) -> bool:
    return resp.status_code >= 400

Try / catch

try:
    response.raise_for_status()
except httpx.HTTPStatusError as e:
    code = e.response.status_code
    if code == 404:
        return None
    if code == 429:
        backoff_and_retry()
    if 500 <= code < 600:
        retry_with_backoff()
    raise

Prevention

When it happens

Trigger: Calling `response.raise_for_status()` after the server returned 4xx (e.g. 401, 404, 422) or 5xx (500, 502, 503).

Common situations: Forgetting that httpx does NOT raise on 4xx/5xx by default (unlike `requests.raise_for_status()` patterns); missing/expired auth tokens producing 401; upstream outages returning 502/503.

Related errors


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