Skip to content

feat(errors): expose upstream provider errors as structured JSON - #489

Open
Destynova2 wants to merge 1 commit into
mainfrom
fix/structured-upstream-error
Open

feat(errors): expose upstream provider errors as structured JSON#489
Destynova2 wants to merge 1 commit into
mainfrom
fix/structured-upstream-error

Conversation

@Destynova2

Copy link
Copy Markdown
Contributor

Fidelity gap (from the competitive analysis)

The analysis flags "provider error detail lost" — Envoy turning a structured
provider error into an empty HTTP 500.

grob is already ahead: it forwards the verbatim upstream status and the
provider body (error.rs ProviderUpstream). But the body reached the client
only as a message string, often prefixed ("anthropic API error: {…}"), so a
client had to re-parse a blob to read the provider's own error.type/code.

Change

into_response now extracts the first embedded JSON object/array from the body
and attaches it as error.upstream_error, structured, alongside the existing
message / provider / upstream_status:

{
  "error": {
    "type": "error",
    "message": "anthropic API error: {…}",
    "provider": "anthropic",
    "upstream_status": 429,
    "upstream_error": { "type": "error", "error": { "type": "rate_limit_error", "message": "slow down" } }
  }
}

Additive and backward compatible — clients reading message are unaffected;
agents that branch on the provider's error type now can, without string-parsing.

Tests

  • extract_leading_json_handles_prefix_pure_and_none — prefixed body, pure JSON, trailing junk, and no-JSON.
  • provider_upstream_forwards_status_and_structured_error — 429 forwarded verbatim + parseable upstream_error.
  • cargo test --lib server::error — 42 passed.

Part of the fidelity sweep (feeds #484).

🤖 Generated with Claude Code

On an upstream failure grob already forwards the verbatim HTTP status and the
provider's body (better than gateways that flatten a structured error into an
empty 500). But the body reached the client only as a `message` string, often
prefixed ("anthropic API error: {…}"), so a client could not read the provider's
own `error.type`/`code` without re-parsing a blob.

`into_response` now extracts the first embedded JSON object/array from the body
and attaches it as `error.upstream_error`, alongside the existing string
`message`, `provider` and `upstream_status`. Additive and backward compatible —
clients reading `message` are unaffected; agents that branch on the provider's
error type now can.

Tested: `extract_leading_json` (prefix / pure / trailing-junk / none) and a 429
ProviderUpstream response asserting status 429 + parseable `upstream_error`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant