we-promise/sure · error · Provider::Openai::Error
No message content found in response
Error message
No message content found in response
What it means
Raised by Provider::Openai::AutoMerchantDetector#extract_merchants_native when a Responses-API reply has no output item of type "message" with content[0].text — the merchant-detection twin of error 350. The native path digs response["output"] for the message item and takes its first content text as the JSON string; nil means only reasoning/tool items, an empty output array, or a nonconforming gateway response. The subsequent JSON.parse has its own error (Invalid JSON in native merchant detection), so this specifically means no text was found at all.
Source
Thrown at app/models/provider/openai/auto_merchant_detector.rb:305
if user_merchants.present?
# Try exact match first
exact_match = user_merchants.find { |m| m[:name] == value }
return exact_match[:name] if exact_match
# Try case-insensitive match
case_match = user_merchants.find { |m| m[:name].to_s.downcase == value.to_s.downcase }
return case_match[:name] if case_match
end
value
end
def extract_merchants_native(response)
# Find the message output (not reasoning output)
message_output = response["output"]&.find { |o| o["type"] == "message" }
raw = message_output&.dig("content", 0, "text")
raise Provider::Openai::Error, "No message content found in response" if raw.nil?
JSON.parse(raw).dig("merchants")
rescue JSON::ParserError => e
raise Provider::Openai::Error, "Invalid JSON in native merchant detection: #{e.message}"
end
def extract_merchants_generic(response)
raw = response.dig("choices", 0, "message", "content")
parsed = parse_json_flexibly(raw)
# Handle different response formats from various LLMs
merchants = parsed.dig("merchants") ||
parsed.dig("results") ||
(parsed.is_a?(Array) ? parsed : nil)
raise Provider::Openai::Error, "Could not find merchants in response" if merchants.nil?
# Normalize field names (some LLMs use different naming)View on GitHub (pinned to e69894adb9)
Solutions
- Log response["output"].map { |o| o["type"] } to confirm whether reasoning-only output is the cause.
- Increase the output token budget or shrink the transaction batch for merchant detection.
- Use a standard chat model (gpt-4.1) for the native path or route the gateway through generic extraction.
- Extend the finder to tolerate adjacent shapes (e.g. summary items carrying content) if the gateway emits them.
Example fix
# before
raw = message_output&.dig("content", 0, "text")
raise Provider::Openai::Error, "No message content found in response" if raw.nil?
# after
raw = message_output&.dig("content", 0, "text")
if raw.nil?
Rails.logger.warn("merchant native output types: #{response.dig("output")&.map { |o| o["type"] }.inspect} status=#{response.dig("status")}")
raise Provider::Openai::Error, "No message content found in response"
end Defensive patterns
Strategy: fallback
Try / catch
begin
merchants = extractor.extract_merchants_native(response)
rescue Provider::Openai::Error => e
raise unless e.message.include?("No message content")
merchants = [] # degrade gracefully; user keeps manual merchant matching
end Prevention
- Log the output item types on native-path failures to distinguish reasoning-only responses from gateway quirks.
- Give merchant detection enough output tokens for the full merchant list plus reasoning.
- Verify custom gateways actually implement /v1/responses message items before using native mode.
When it happens
Trigger: o-series reasoning model spending the entire output budget on reasoning items; custom OpenAI-compatible gateway returning a Responses body without a message item; model refusal with empty content; max_output_tokens truncation before the message item is emitted.
Common situations: Merchant detection switched to a reasoning model with a tight budget; gateway partially implementing /v1/responses; long merchant lists pushing output past the limit; upstream Responses payload changes.
Related errors
- No message content found in response
- OpenAI stream ended without a completion event. This usually
- No response from AI
- {e.record.errors.full_messages.to_sentence.presence || e.mes
- Assistant returned neither text nor tool calls
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/b34d15406b677f15.
Report an issue: GitHub.