Skip to content

fix(bedrock): handle EOF consistently, fix stale events, and surface exceptions#303

Open
rishabh-emergent wants to merge 1 commit intoanthropics:mainfrom
rishabh-emergent:fix/bedrock-stream-eof-and-stale-event
Open

fix(bedrock): handle EOF consistently, fix stale events, and surface exceptions#303
rishabh-emergent wants to merge 1 commit intoanthropics:mainfrom
rishabh-emergent:fix/bedrock-stream-eof-and-stale-event

Conversation

@rishabh-emergent
Copy link
Copy Markdown

Summary

Three fixes for the Bedrock EventStream decoder in bedrock/bedrock.go.

Bug 1: EOF exposed as error (fixes #110)

The SSE decoder (eventStreamDecoder) returns nil from scn.Err() on normal stream end. The EventStream decoder (eventstreamDecoder) exposes io.EOF as an error via Err(). This forces consumers to special-case Bedrock EOF handling.

// Before: EOF stored as error
e.err = err
return false

// After: EOF filtered, matching SSE decoder behavior
if err == io.EOF {
    return false
}
e.err = err
return false

Bug 2: Stale event on non-chunk event types

When eventType != "chunk", e.evt is not updated but Next() returns true. Consumers re-process the previous event's data.

// Before: falls through to return true with stale e.evt
if eventType.String() == "chunk" {
    // ... update e.evt
}
// return true ← stale event!

// After: skip non-chunk events
if eventType.String() == "chunk" {
    // ... update e.evt
    return true
}
return e.Next() // skip, try next event

Bug 3: ExceptionMessageType silently dropped (fixes #71)

Go case statements don't fall through. ExceptionMessageType had its own handler that never reached ErrorMessageType, causing Bedrock exceptions to be silently dropped.

// Before: two separate cases, exception handler doesn't fall through
case eventstreamapi.ExceptionMessageType:
    // ... sets e.err, returns false
case eventstreamapi.ErrorMessageType:
    // ... never reached for exceptions

// After: merged into single case
case eventstreamapi.ExceptionMessageType, eventstreamapi.ErrorMessageType:
    // handles both uniformly

Impact

These bugs cause silent data loss when streaming from Bedrock:

We hit all three in production (391 errors/day) when Bedrock streams were prematurely terminated — the EOF was silently swallowed, and the incomplete response caused downstream failures.

Tests

All existing tests pass: go test ./bedrock/... -v

…exceptions

Three fixes for the Bedrock EventStream decoder:

1. Filter io.EOF on normal stream end (fixes anthropics#110)

   The SSE decoder returns nil from scn.Err() on normal EOF, but the
   EventStream decoder exposes io.EOF as an error via Err(). This
   inconsistency forces consumers to special-case Bedrock EOF handling.
   Now EOF is filtered at the source, matching SSE decoder behavior.

2. Prevent stale event on non-chunk event types

   When eventType != "chunk", e.evt was not updated but Next() returned
   true, causing consumers to re-process the previous event. Now
   non-chunk events are skipped via recursive Next() call.

3. Surface ExceptionMessageType errors (fixes anthropics#71)

   Go case statements don't fall through. The ExceptionMessageType case
   had its own handler that never reached the ErrorMessageType handler,
   causing Bedrock exceptions to be silently dropped. Merged both cases
   into a single handler.
@rishabh-emergent rishabh-emergent requested a review from a team as a code owner March 25, 2026 11:14
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.

Bedrock streaming decoder error not returned Error handling code for Bedrock doesn't work for exception message types

1 participant