Skip to content

fix(bedrock): unify streaming error handling for in-band and out-of-band errors#1479

Open
sevakva wants to merge 2 commits intoanthropics:mainfrom
sevakva:fix/bedrock-streaming-errors
Open

fix(bedrock): unify streaming error handling for in-band and out-of-band errors#1479
sevakva wants to merge 2 commits intoanthropics:mainfrom
sevakva:fix/bedrock-streaming-errors

Conversation

@sevakva
Copy link
Copy Markdown

@sevakva sevakva commented Apr 30, 2026

Summary

Closes #1472. Closes #1477. Supersedes #1475.

Two stream-error paths in lib/bedrock failed to surface as typed APIStatusError subclasses. This PR routes both through the existing event="error" SSE path and adds a body-aware exception dispatch.

Bugs

#1472 — HTTP 200 stream where the first SSE chunk is an in-band error envelope ({"type":"error","error":{...}}), reliably reproducible with cross-region inference profiles. The decoder hardcoded event="completion", so the streaming layer parsed it as RawMessageStartEvent(message=None) and crashed with AttributeError: 'NoneType' object has no attribute 'model'.

#1477 — Out-of-band exception frame (:message-type: exception with a :exception-type header). botocore parses these with status_code != 200. The decoder raised a bare ValueError, uncatchable as APIStatusError.

Bundled: HTTP 529 → OverloadedError, matching anthropic._client.

Changes

Decoder normalizes both shapes into event="error" and translates Bedrock :exception-type to the Anthropic error.type:

:exception-type error.type Class
throttlingException rate_limit_error RateLimitError
serviceUnavailableException overloaded_error OverloadedError
internalServerException api_error InternalServerError
modelStreamErrorException api_error InternalServerError
modelTimeoutException api_error InternalServerError
validationException invalid_request_error BadRequestError

Unknown types pass through verbatim → generic APIStatusError. The event status_code is not used for class selection (botocore hardcodes it to 400 for any exception frame); :exception-type is the source of truth.

Client_make_status_error gains a body-aware pre-dispatch keyed on body.error.type. This is what lets in-band HTTP 200 errors reach the right subclass. The lookup table uses ErrorType literals from anthropic.types.shared.

The body dispatch is additive — non-stream 4xx/5xx already produce the correct subclass via HTTP status; the new path only kicks in when the body carries an error.type the status fallback would miss.

Tests

  • tests/lib/test_bedrock_stream_decoder.py — decoder unit tests, parametrized over the full mapping table.
  • tests/lib/streaming/test_bedrock_stream_errors.py — E2E via httpx.MockTransport with hand-encoded eventstream frames, sync + async, plus 529 parity.

sevakva added 2 commits April 30, 2026 11:10
Two streaming error paths previously failed to surface as typed
APIStatusError subclasses:

- HTTP 200 streams where the first SSE chunk is an in-band error frame
  (e.g. cross-region inference profile rate limits). The decoder
  hardcoded event="completion", so the streaming layer parsed the
  payload as RawMessageStartEvent with message=None and crashed with
  AttributeError (anthropics#1472).
- Out-of-band stream errors where botocore parses an EventStreamMessage
  with status_code != 200 and a :exception-type header (e.g.
  internalServerException, throttlingException). The decoder raised a
  bare ValueError that bypassed retry/error handling (anthropics#1477).

Funnel both shapes through the existing event="error" SSE path:

- Decoder normalizes Bedrock :exception-type into Anthropic error.type
  (throttlingException → rate_limit_error,
  serviceUnavailableException → overloaded_error,
  internalServerException/modelStreamError/modelTimeout → api_error,
  validationException → invalid_request_error). Unknown types pass
  through verbatim and fall back to a generic APIStatusError.
- _make_status_error gains a body-aware pre-dispatch on
  body.error.type so in-band errors on HTTP 200 select the right
  exception class (RateLimitError, OverloadedError, etc.) instead of
  being misclassified as success.
- HTTP 529 → OverloadedError, matching anthropic._client behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant