xtekky/gpt4free · error · ResponseStatusError

Response {response.status}: {'HTML content' if is_html else

Error message

Response {response.status}: {'HTML content' if is_html else message}

What it means

The fallback branch of g4f's async raise_for_status: any non-ok status that is not 429/402/401/403-with-known-fingerprint/502/504/400-bad-key becomes ResponseStatusError. When the body is HTML the message is the literal 'HTML content' instead of the page, so huge challenge/error pages don't leak into exceptions.

Source

Thrown at g4f/requests/raise_for_status.py:83

    if message is None or is_html:
        if response.status == 520:
            message = "Unknown error (Cloudflare)"
    if response.status in (429, 402):
        raise RateLimitError(f"Response {response.status}: {message}")
    if response.status == 401:
        raise MissingAuthError(f"Response {response.status}: {message}")
    if response.status == 403 and is_cloudflare(message):
        raise CloudflareError(f"Response {response.status}: Cloudflare detected")
    elif response.status == 403 and (is_openai(message) or is_lmarena(message)):
        raise MissingAuthError(f"Response {response.status}: OpenAI Bot detected")
    elif response.status == 502:
        raise ResponseStatusError(f"Response {response.status}: Bad Gateway")
    elif response.status == 504:
        raise RateLimitError(f"Response {response.status}: Gateway Timeout ")
    elif response.status == 400 and "API key not valid" in message:
        raise MissingAuthError(f"Response {response.status}: Invalid API key")
    else:
        raise ResponseStatusError(
            f"Response {response.status}: {'HTML content' if is_html else message}"
        )


def raise_for_status(
    response: Union[Response, StreamResponse, ClientResponse, RequestsResponse],
    message: str = None,
):
    if hasattr(response, "status"):
        return raise_for_status_async(response, message)
    if response.ok:
        return
    is_html = False
    if message is None:
        is_html = response.headers.get("content-type", "").startswith(
            "text/html"
        ) or response.text.startswith("<!DOCTYPE")
        message = response.text

View on GitHub (pinned to 973504e177)

Solutions

  1. Read the numeric status in the message ('Response {status}: ...') to identify the real cause.
  2. For 404/400: update g4f (pip install -U g4f) — provider endpoints and payloads change often.
  3. For 5xx: retry later or switch provider.
  4. For 400: validate the model name and message format against the provider's current API.
Defensive patterns

Strategy: try-catch

Type guard

from g4f.errors import ResponseStatusError, CloudflareError, RateLimitError
def is_generic_status(err: BaseException) -> bool:
    return isinstance(err, ResponseStatusError) and not isinstance(err, (CloudflareError, RateLimitError))

Try / catch

from g4f.errors import ResponseStatusError
try:
    result = await client.chat.completions.async_create(...)
except ResponseStatusError as e:
    status = int(str(e).split(':')[0].split()[-1])
    if status >= 500:
        result = await retry_later()
    elif status in (400, 404):
        raise RuntimeError(f'Provider API mismatch: {e}') from e
    else:
        raise

Prevention

When it happens

Trigger: Statuses like 400 (other validation errors), 404 (dead provider endpoint), 405, 408, 418, 500, 501, 503, or any HTML error page not matching the Cloudflare/OpenAI fingerprints.

Common situations: Provider changed its URL/API shape (404); upstream 500 crash; request payload rejected with 400 (bad model name, malformed messages); unmaintained provider in an old g4f version.

Related errors


AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14). Data as JSON: /api/errors/b045b90684f720a9. Report an issue: GitHub.