Skip to content

fix: nil-safe content.strip in chat_completion final-response paths#49

Merged
obie merged 2 commits into
mainfrom
fix/nil-content-strip
Apr 30, 2026
Merged

fix: nil-safe content.strip in chat_completion final-response paths#49
obie merged 2 commits into
mainfrom
fix/nil-content-strip

Conversation

@obie
Copy link
Copy Markdown
Contributor

@obie obie commented Apr 30, 2026

Symptom

NoMethodError: undefined method 'strip' for nil
  from lib/raix/chat_completion.rb:226

A long-running chat completion crashed with the above when the LLM returned a final assistant message containing "content": null. The same input on retry succeeded (the model returned non-nil content the second time), confirming the bug is non-deterministic and depends on what the provider emits.

When it happens

Some providers — notably Gemini under certain stop conditions (length-limit hit, tool-exhaustion fallback, etc.) — return:

{ "choices": [ { "message": { "content": null, "tool_calls": [] } } ] }

Raix::ChatCompletion calls content.strip on the final response in three places without guarding against nil:

  • lib/raix/chat_completion.rb:175 (max_tool_calls exceeded path)
  • lib/raix/chat_completion.rb:218 (stop_tool_calls_and_respond! path)
  • lib/raix/chat_completion.rb:226 (no tool_calls fall-through path)

When content is nil, nil.strip raises NoMethodError and the whole completion blows up.

Fix

Change content.stripcontent.to_s.strip at each of the three call sites. nil.to_s is "", so a nil response now coerces to an empty string instead of raising. No other behavior changes — non-nil content still strips identically.

Tests

Added spec/raix/nil_content_spec.rb covering each of the three branches by stubbing ruby_llm_request to return {"choices" => [{"message" => {"content" => nil, ...}}]}:

  • Plain final response with nil content (line 226 path)
  • Forced final response after max_tool_calls exceeded with nil content (line 175 path)
  • Forced final response after stop_tool_calls_and_respond! with nil content (line 218 path)

Each spec verifies chat_completion returns "" instead of raising NoMethodError. Confirmed the specs fail on main and pass with the fix applied.

bundle exec rake runs clean: 96 examples, 0 failures, 37 files rubocop-clean.

obie and others added 2 commits April 30, 2026 13:45
Raix raised NoMethodError when an LLM returned a final assistant
message with null content (e.g. Gemini on certain stop conditions).
Three call sites in chat_completion.rb did `content.strip` without
guarding against nil; now use `content.to_s.strip`.
CI on main has been failing since the 2.0.2 release because the
gemspec version bump didn't include a corresponding lockfile update.
This unblocks deployment-mode bundle install on CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@obie obie merged commit 6652b5f into main Apr 30, 2026
1 check passed
@obie obie deleted the fix/nil-content-strip branch April 30, 2026 11:59
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