Skip to content

Fix Bedrock streaming dropping all chunks when Faraday env is nil#813

Open
chen-anders wants to merge 2 commits into
crmne:mainfrom
wistia:anders/fix-bedrock-streaming-nil-env
Open

Fix Bedrock streaming dropping all chunks when Faraday env is nil#813
chen-anders wants to merge 2 commits into
crmne:mainfrom
wistia:anders/fix-bedrock-streaming-nil-env

Conversation

@chen-anders

@chen-anders chen-anders commented Jun 16, 2026

Copy link
Copy Markdown

What this does

Fixes a bug where Bedrock ConverseStream responses come back completely empty (zero chunks, blank content, nil token counts) even though the HTTP request succeeds with status 200.

The Faraday 2 on_data callback gates chunk parsing on env&.status == 200. With the net_http adapter, env is nil during streaming (the status is not yet known when chunks arrive), so this condition is false for every chunk. Each valid AWS event-stream frame is then routed to the failed-response handler, which tries to JSON.parse binary eventstream bytes, fails, logs a "failed stream error chunk" debug line, and silently drops it. The result is an empty streamed response.

This affects any app on Faraday 2.x + the net_http adapter (Faraday's default) using Bedrock streaming.

Fix: invert the guard so a nil env (status unknown) falls through to normal chunk parsing, and only a present env reporting a non-200 status is treated as a failure. Applied in both spots that build the v2 on_data proc:

  • RubyLLM::Streaming::FaradayHandlers.v2_on_data (shared SSE handler)
  • RubyLLM::Protocols::Converse::Streaming#stream_response (Bedrock ConverseStream)

Reproduction

chat = RubyLLM.chat(model: "us.anthropic.claude-sonnet-4-6", provider: "bedrock", assume_model_exists: true)

# Non-streaming — works:
chat.ask("Say BANANA only.").content          # => "BANANA"

# Streaming — broken before this fix:
chunks = []
resp = chat.ask("Say BANANA only.") { |c| chunks << c }
chunks.size    # => 0   (expected: > 0)
resp.content   # => ""   (expected: "BANANA")

With Faraday 2.x + net_http, the debug log shows the data arriving fine ({"delta":{"text":"BANANA"}}, usage tokens, status 200) but every frame hitting "Failed Bedrock stream error chunk". After the fix: chunks.size => 5, resp.content => "BANANA", tokens populated.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Performance improvement

Scope check

  • I read the Contributing Guide
  • This aligns with RubyLLM's focus on LLM communication
  • This isn't application-specific logic that belongs in user code
  • This benefits most users, not just my specific use case

Quality check

  • I ran overcommit --install and all hooks pass
  • I tested my changes thoroughly
    • For provider changes: Re-recorded VCR cassettes with bundle exec rake vcr:record[provider_name]
    • Added unit tests covering nil-env / 200 / non-200 routing for both code paths (spec/ruby_llm/streaming_spec.rb, spec/ruby_llm/protocols/converse/streaming_spec.rb)
  • I updated documentation if needed
  • I didn't modify auto-generated files manually (models.json, aliases.json)

AI-generated code

  • I used AI tools to help write this code
  • I have reviewed and understand all generated code (required if above is checked)

API changes

  • Breaking change
  • New public methods/classes
  • Changed method signatures
  • No API changes

@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.88%. Comparing base (80fe294) to head (a1093e9).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #813   +/-   ##
=======================================
  Coverage   82.88%   82.88%           
=======================================
  Files         142      142           
  Lines        6574     6574           
  Branches     1148     1148           
=======================================
  Hits         5449     5449           
- Misses        663      664    +1     
+ Partials      462      461    -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The Faraday 2 on_data callback gates chunk parsing on
`env&.status == 200`. With the net_http adapter, env is nil during
streaming (the status is not yet known), so this is false for every
chunk. Each valid AWS event-stream frame is then routed to the
failed-response handler, which tries to JSON-parse binary eventstream
bytes, fails, logs a "failed stream error chunk" debug line, and drops
it. The result: ConverseStream responses come back completely empty
(zero chunks, blank content, nil token counts) even though the HTTP
request itself succeeds with status 200.

Invert the guard so a nil env (status unknown) falls through to normal
chunk parsing, and only a present env reporting a non-200 status is
treated as a failure. Applied in both the Converse streaming module and
the shared FaradayHandlers.v2_on_data helper.
Cover the regression where a nil Faraday env (Faraday 2 + net_http
during streaming) caused every chunk to be routed to the
failed-response handler and discarded. Tests exercise both the shared
FaradayHandlers.v2_on_data helper and the Converse stream_response
on_data proc directly, asserting that a nil env and a 200 env both
parse the chunk while a present non-200 env triggers failure handling.
@chen-anders chen-anders force-pushed the anders/fix-bedrock-streaming-nil-env branch from 8321563 to a1093e9 Compare June 16, 2026 03:50
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