Skip to content

Fix streaming error parsing crash on non-Hash JSON error bodies - #840

Merged
crmne merged 3 commits into
crmne:mainfrom
Niraj22:fix-streaming-error-non-hash-body
Aug 10, 2026
Merged

crmne merged 3 commits into
crmne:mainfrom
Niraj22:fix-streaming-error-non-hash-body

Conversation

@Niraj22

@Niraj22 Niraj22 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

What this does

Fixes #837. When a streaming request fails and the error body parses to something other than the typed {"error": {"type": …, "message": …}} hash — a bare JSON string, or a string error value like {"error": "msg"} (xAI/Grok shape) — parse_streaming_error crashed with TypeError: String does not have #dig method instead of surfacing the provider's message. The same bodies were already handled fine on the non-streaming path.

The old guard return unless error_data['error'] did substring matching on string bodies (String#[]), and dig('error', 'type') recursed into string error values — hence the crash.

This adds shape guards before the hash lookups in the chat_completions, anthropic, and gemini protocols plus the default fallback in Streaming. Untyped bodies return a nil status so build_stream_error_response falls back to the real HTTP status; the user-facing message still comes from Provider#parse_error, which already handles string bodies.

Type of change

  • Bug fix

How tested

Regression specs per protocol (typed object, bare string body, string error value) plus end-to-end specs through handle_failed_response asserting the provider's actual message surfaces in the raised error. Full suite: 1889 examples, 0 failures.

@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.75000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.50%. Comparing base (1a5c8ac) to head (c49fe9c).

Files with missing lines Patch % Lines
lib/ruby_llm/providers/openrouter.rb 85.71% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #840      +/-   ##
==========================================
+ Coverage   95.44%   95.50%   +0.06%     
==========================================
  Files         182      182              
  Lines        9106     9124      +18     
  Branches     1480     1490      +10     
==========================================
+ Hits         8691     8714      +23     
+ Misses        188      184       -4     
+ Partials      227      226       -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.

Niraj22 added a commit to Niraj22/ruby_llm that referenced this pull request Jul 25, 2026
Codecov flagged 4 uncovered lines on PR crmne#840: the Hash-shaped-error
fallback in the anthropic protocol (type other than overloaded_error)
and the server_error/default branches in the chat_completions protocol
were never exercised by the regression specs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
crmne pushed a commit to Niraj22/ruby_llm that referenced this pull request Aug 10, 2026
Codecov flagged 4 uncovered lines on PR crmne#840: the Hash-shaped-error
fallback in the anthropic protocol (type other than overloaded_error)
and the server_error/default branches in the chat_completions protocol
were never exercised by the regression specs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@crmne
crmne force-pushed the fix-streaming-error-non-hash-body branch from 9feec2d to 8d87a4a Compare August 10, 2026 21:26
Niraj22 and others added 3 commits August 10, 2026 23:38
parse_streaming_error assumed the error body parses to a Hash with a
Hash 'error' value. A bare JSON string body, or a string error value
like {"error": "msg"} (xAI/Grok shape), crashed with TypeError:
String does not have #dig instead of surfacing the provider's message.

Guard the body shape before digging, in the chat_completions, anthropic
and gemini protocols plus the default fallback. Untyped bodies return a
nil status so the real HTTP status from the response env wins.

Fixes crmne#837
Codecov flagged 4 uncovered lines on PR crmne#840: the Hash-shaped-error
fallback in the anthropic protocol (type other than overloaded_error)
and the server_error/default branches in the chat_completions protocol
were never exercised by the regression specs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@crmne
crmne force-pushed the fix-streaming-error-non-hash-body branch from 8d87a4a to c49fe9c Compare August 10, 2026 21:39
@crmne
crmne merged commit 9cb240c into crmne:main Aug 10, 2026
23 checks passed
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.

Streaming: parse_streaming_error crashes with 'TypeError: String does not have #dig' on a non-Hash JSON error body

2 participants