Disclosure: issue identified and reproduced with the help of Claude Opus 4.7
Summary
When aibridge proxies a streaming /v1/messages request to AWS Bedrock and the upstream stream completes successfully, the interception is recorded as a failure with error=EOF. The client receives all events correctly — everything appears to work — but the server logs a misleading warning, the
aibridge_interceptions_total{status="failed"} metric is incremented, and the OTel span is marked Error.
Repro
Any successful /v1/messages SSE request routed through Bedrock. Example log line:
warn coderd.aibridged.pool: interception failed route=/v1/messages provider=anthropic streaming=true error=EOF
Root cause
The Anthropic Go SDK's Bedrock event-stream decoder violates Go's iterator convention.
// bedrock/bedrock.go:
msg, err := e.Decoder.Decode(e.rc, nil)
if err != nil {
e.err = err // io.EOF on clean end gets captured here
return false
}
Stream.Err() therefore returns io.EOF on clean stream completion, instead of nil (compare bufio.Scanner.Err(), the sibling SSE decoder, etc.).
In aibridge/intercept/messages/streaming.go, mapStreamError returns nil for this EOF (via IsUnrecoverableError), but the else if streamErr != nil capture (added in #24836) then records EOF as interceptionErr, which bridge.go logs as "interception failed" and counts as a failed interception.
The conflation also exists in eventstream.IsConnError, which includes io.EOF alongside EPIPE/ECONNRESET — semantically wrong: EOF is end-of-stream from upstream reads, while client disconnects manifest as EPIPE/ECONNRESET on writes.
Same pattern in aibridge/intercept/chatcompletions/streaming.go.
Impact
- Misleading WARN-level logs on every successful Bedrock streaming completion.
- aibridge_interceptions_total{status="failed"} over-counts; status="completed" under-counts.
- OTel spans for successful interceptions are marked Error.
- No user-visible effect on the response itself.
Suggested fix
if err != nil {
if !errors.Is(err, io.EOF) { e.err = err }
return false
}
Disclosure: issue identified and reproduced with the help of Claude Opus 4.7
Summary
When aibridge proxies a streaming
/v1/messagesrequest to AWS Bedrock and the upstream stream completes successfully, the interception is recorded as a failure witherror=EOF. The client receives all events correctly — everything appears to work — but the server logs a misleading warning, theaibridge_interceptions_total{status="failed"}metric is incremented, and the OTel span is marked Error.Repro
Any successful
/v1/messagesSSE request routed through Bedrock. Example log line:Root cause
The Anthropic Go SDK's Bedrock event-stream decoder violates Go's iterator convention.
Stream.Err() therefore returns io.EOF on clean stream completion, instead of nil (compare bufio.Scanner.Err(), the sibling SSE decoder, etc.).
In aibridge/intercept/messages/streaming.go, mapStreamError returns nil for this EOF (via IsUnrecoverableError), but the else if streamErr != nil capture (added in #24836) then records EOF as interceptionErr, which bridge.go logs as "interception failed" and counts as a failed interception.
The conflation also exists in eventstream.IsConnError, which includes io.EOF alongside EPIPE/ECONNRESET — semantically wrong: EOF is end-of-stream from upstream reads, while client disconnects manifest as EPIPE/ECONNRESET on writes.
Same pattern in aibridge/intercept/chatcompletions/streaming.go.
Impact
Suggested fix