Skip to content

Surface provider error bodies in image generation failures#132

Merged
adambalogh merged 1 commit into
mainfrom
claude/tee-400-bad-request-dqwda4
Jul 16, 2026
Merged

Surface provider error bodies in image generation failures#132
adambalogh merged 1 commit into
mainfrom
claude/tee-400-bad-request-dqwda4

Conversation

@adambalogh

Copy link
Copy Markdown
Contributor

Problem

A seedream-5.0-lite request through /v1/chat/completions failed with:

{
    "error": "Client error '400 Bad Request' for url 'https://ark.ap-southeast.bytepluses.com/api/v3/images/generations'\nFor more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400",
    "exception_type": "HTTPStatusError"
}

The gateway's image-generation path calls resp.raise_for_status(), and httpx's HTTPStatusError message only contains the status code and URL. ByteDance ARK (like every OpenAI-compatible provider) puts the actual rejection reason — invalid parameter, prompt length limit, content-filter refusal — in the JSON response body, which was discarded. That makes 400s like this one undiagnosable from either the enclave logs or the client-facing error payload.

Change

  • Add _provider_error_detail() in image_generation.py: extracts a compact detail string from an error response, preferring the {"error": {"code", "message"}} shape (also handles a string error field), falling back to the raw body, capped at 600 chars so a pathological body can't flood logs.
  • Add _raise_for_status_with_detail() and use it at both raise_for_status() call sites — the /images/generations / /images/edits request and the hosted-URL fetch. It re-raises httpx.HTTPStatusError (so exception_type bucketing is unchanged) with the provider detail appended and httpx's MDN-link footer dropped.

With this, the same failure will read e.g.:

Client error '400 Bad Request' for url '…/images/generations' — provider response: InvalidParameter: The request parameter `prompt` exceeds the limit.

Testing

  • New TestProviderErrorDetail cases pin: error body surfaced from generate_images, JSON error.code/error.message extraction, string error field, non-JSON fallback, empty body, truncation, and the streamed URL-fetch error path.
  • Full suite: 178 tests, all passing except the pre-existing test_ohttp import error (pytest not installed in the env, unrelated).
  • make lint (ruff + mypy) clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Mhie33mj7RR62J61vHYjoF


Generated by Claude Code

A failed /images/generations call (e.g. seedream-5.0-lite returning 400
from ARK) currently raises httpx's bare HTTPStatusError, whose message is
just the status code and URL. The provider's actual rejection reason
(invalid parameter, prompt limit, content-filter refusal, ...) lives in
the response body and was discarded, making these failures undiagnosable
from logs or the client-facing error payload.

Wrap both raise_for_status() call sites (the generations/edits request
and the hosted-URL fetch) so the raised error carries a compact detail
extracted from the body — preferring the JSON error.code/error.message
shape, falling back to the raw text, capped at 600 chars — and drop the
MDN-link footer from httpx's default message.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Mhie33mj7RR62J61vHYjoF
@adambalogh
adambalogh marked this pull request as ready for review July 16, 2026 15:00
@adambalogh
adambalogh merged commit e54cf62 into main Jul 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants