Bedrock-derived error contract across every client surface - #13
Merged
Conversation
…dline split ErrClass (consts) is the Bedrock-derived closed classification set from the gateway error contract; ErrCode stays internal. StreamChunk.error becomes a structured StreamError (class + message + original upstream status). The pump now delivers a terminal error frame on committed mid-stream aborts instead of ending silently; client disconnects stay silent. Upstream deadlines split off as FED_RESP_TIMEOUT (internal 502 keeps failover, renders as 408). vendor_error accepts the AWS auth layer's capitalized Message field.
One classify-then-render path replaces the three ad-hoc serializers: OpenAI and Anthropic envelopes both carry the classification code, every HTTP error carries x-amzn-errortype plus the CORS exposure, and external statuses come from the classification (terminal vendor errors land as 424 ModelError with original_status_code/resource_name instead of leaking the upstream status). Json extractor rejections, unknown routes, and wrong methods render in the envelope instead of axum's bare responses. Realtime WS uses one nested in-band error shape with real classes from the gate; SSE error frames carry class-driven types (Responses uses the official flat error event with sequence_number). Terminal upstream 429/503 keep Throttling/ ServiceUnavailable semantics; the wrong-surface factory error is a 400 validation error, not a 501 internal.
Simplify convergence from the four-lens pass: one render core in views (bare_status + class_response shared by every envelope), the 424-extras rule lives once as GatewayError::original_status, StreamError::from_error takes ownership and moves the message, the pump's decode-fault branch reuses StreamFault instead of hand-rolling both error forms, the ApiJson/AnthJson extractors share one body, the chat SSE frame reuses openai_error_body, and the realtime send_err alias is gone. abort_frame's live-channel invariant is now a debug_assert; ServiceQuotaExceeded documents that it is contract- reserved with no construction site yet; the wrong-surface e2e asserts the classification, not just the status.
Idle-gap timeouts now assert the FED_RESP_TIMEOUT code (the ModelTimeout classification link), oversized bodies assert the 413 envelope, and the realtime mock session asserts the nested validation error frame. Spec section references in code comments replaced with self-contained wording — the numbered contract lives in a private repo public comments must not point at.
Non-timeout body-read failures join the upstream fault family (502 FED_RESP_RPC_FAILED -> external 424) instead of masquerading as internal 500s, with wire tests for both the timeout and mid-body-break paths. The timeout-vs-rpc code split now lives once (transport::upstream_fault_code); the pump's decode-fault arms build one StreamFault; the Anthropic error body has one owner shared by HTTP and SSE like the OpenAI side; realtime quota denials use a named closure beside throttled; wire tests share one serve_router bootstrap. Message ingest stops cloning plain-text content just to drop the original (into_text_and_parts / blocks_text move it). Comment pass: two docs tightened to the one-line budget, limit_denied's stale every-limit claim scoped to throttling now that quota_denied exists. Whole-repo sweep found no layout, unwrap, or comment-budget violations; DAG ordering guards adjudicated keep (runtime-enforced topology).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Every client-visible error now carries one classification from a closed, Bedrock-derived set, exposed on two machine channels: the
x-amzn-errortyperesponse header (PascalCase, e.g.ThrottlingException) and the bodycodefield (snake_case, e.g.throttling_exception). Envelope shapes stay surface-native — OpenAI surfaces keep{"error":{...}},/v1/messageskeeps the Anthropic shape, realtime WS uses one nested in-band event — so official SDKs keep working while custom clients get a single error handler for every endpoint (see docs/api.md).Highlights
ErrClassclosed set (consts): name/code/status plus per-envelope coarse types;ErrCodegoes internal-only (logs/metrics).ModelErrorExceptionwithoriginal_status_code(explicitly recorded, never a synthesized status) andresource_name(attached on the DAG error path only).FED_RESP_TIMEOUTand render 408; non-timeout body faults join the upstream fault family instead of masquerading as internal 500s.service_quota_exceeded_exception(non-retryable, does not feed abuse tiers); transient rate limits stay 429.sequence_number; chat frames carry the envelope plusoriginal_status_code.vendor_errortolerates the AWS auth layer's capitalizedMessage; message ingest stops cloning plain-text content it immediately drops.Breaking change
External error bodies change:
error.codeswitches from the stringified numeric code to the classification name,type: "gateway_error"is gone, and terminal vendor errors return 424 instead of the upstream's raw status. Clients should classify via the header orcodeand retry only on 408/429/500/503.Evidence