HKUDS/DeepTutor · error · LLMAPIError

OpenAI stream error: {error_text}

Error message

OpenAI stream error: {error_text}

What it means

Raised in _openai_stream when the SSE POST returns a non-2xx status before any chunks are yielded (the response_format retry branch has already been exhausted or did not apply). The response body text is embedded in LLMAPIError along with status_code and provider, mirroring the server's error message.

Source

Thrown at deeptutor/services/llm/cloud_provider.py:568

                    retry_attempt == 0
                    and resp.status == 400
                    and "response_format" in attempt_data
                    and _looks_like_unsupported_response_format(error_text)
                ):
                    logger.warning(
                        "Provider %s rejected response_format for model %s "
                        "(HTTP 400); retrying stream without it. Body: %s",
                        binding,
                        model,
                        error_text[:200],
                    )
                    disable_response_format_at_runtime(binding, model)
                    attempt_data = dict(attempt_data)
                    attempt_data.pop("response_format", None)
                    await resp_cm.__aexit__(None, None, None)
                    continue
                await resp_cm.__aexit__(None, None, None)
                raise LLMAPIError(
                    f"OpenAI stream error: {error_text}",
                    status_code=resp.status,
                    provider=binding or "openai",
                )
            except BaseException:
                await resp_cm.__aexit__(None, None, None)
                raise

        try:
            # Track thinking block state for streaming
            in_thinking_block = False
            thinking_buffer = ""

            async for line in resp.content:
                line_str = line.decode("utf-8").strip()
                if not line_str or not line_str.startswith("data:"):
                    continue

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Read e.status_code and the embedded error_text to identify the server-side reason.
  2. Correct auth/model/base_url per the status (401 → key, 404 → url/model, 429 → backoff).
  3. For 429, retry with backoff or enable KeyPool rotation before re-streaming.
  4. If a gateway rejects streaming, call non-streaming complete() as a fallback path.

Example fix

// before
async for chunk in stream(prompt=p, model=m):
    ...

# after
try:
    async for chunk in stream(prompt=p, model=m):
        ...
except LLMAPIError as e:
    if e.status_code == 429:
        await asyncio.sleep(30)
    raise
Defensive patterns

Strategy: retry

Validate before calling

# Nothing to validate client-side beyond auth/model; do a cheap reachability check:
async with aiohttp.ClientSession() as s:
    async with s.get(base_url or "https://api.openai.com/v1/models") as r:
        if r.status in (401, 404):
            raise RuntimeError("stream pre-check failed")

Try / catch

try:
    async for chunk in stream(prompt=p, model=m):
        buf.append(chunk)
except LLMAPIError as e:
    if e.status_code in (429, 500, 503):
        await asyncio.sleep(2 ** attempt)
        # retry loop
    else:
        raise

Prevention

When it happens

Trigger: Starting a stream against an endpoint that immediately replies 401 (bad key), 400 (invalid model/param), or 429; mid-stream failures typically surface differently — this fires on the initial status check.

Common situations: Expired API key discovered only when streaming; model name typo'd for the endpoint; strict gateways rejecting stream:true; rate limits hit before stream start.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/0d267e04a2266a66. Report an issue: GitHub.