{"record":{"id":"b34d15406b677f15","repo":"we-promise/sure","slug":"no-message-content-found-in-response-b34d15","errorCode":null,"errorMessage":"No message content found in response","messagePattern":"No message content found in response","errorType":"exception","errorClass":"Provider::Openai::Error","httpStatus":null,"severity":"error","filePath":"app/models/provider/openai/auto_merchant_detector.rb","lineNumber":305,"sourceCode":"      if user_merchants.present?\n        # Try exact match first\n        exact_match = user_merchants.find { |m| m[:name] == value }\n        return exact_match[:name] if exact_match\n\n        # Try case-insensitive match\n        case_match = user_merchants.find { |m| m[:name].to_s.downcase == value.to_s.downcase }\n        return case_match[:name] if case_match\n      end\n\n      value\n    end\n\n    def extract_merchants_native(response)\n      # Find the message output (not reasoning output)\n      message_output = response[\"output\"]&.find { |o| o[\"type\"] == \"message\" }\n      raw = message_output&.dig(\"content\", 0, \"text\")\n\n      raise Provider::Openai::Error, \"No message content found in response\" if raw.nil?\n\n      JSON.parse(raw).dig(\"merchants\")\n    rescue JSON::ParserError => e\n      raise Provider::Openai::Error, \"Invalid JSON in native merchant detection: #{e.message}\"\n    end\n\n    def extract_merchants_generic(response)\n      raw = response.dig(\"choices\", 0, \"message\", \"content\")\n      parsed = parse_json_flexibly(raw)\n\n      # Handle different response formats from various LLMs\n      merchants = parsed.dig(\"merchants\") ||\n                  parsed.dig(\"results\") ||\n                  (parsed.is_a?(Array) ? parsed : nil)\n\n      raise Provider::Openai::Error, \"Could not find merchants in response\" if merchants.nil?\n\n      # Normalize field names (some LLMs use different naming)","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/openai/auto_merchant_detector.rb#L287-L323","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nraw = message_output&.dig(\"content\", 0, \"text\")\nraise Provider::Openai::Error, \"No message content found in response\" if raw.nil?\n\n# after\nraw = message_output&.dig(\"content\", 0, \"text\")\nif raw.nil?\n  Rails.logger.warn(\"merchant native output types: #{response.dig(\"output\")&.map { |o| o[\"type\"] }.inspect} status=#{response.dig(\"status\")}\")\n  raise Provider::Openai::Error, \"No message content found in response\"\nend","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  merchants = extractor.extract_merchants_native(response)\nrescue Provider::Openai::Error => e\n  raise unless e.message.include?(\"No message content\")\n  merchants = [] # degrade gracefully; user keeps manual merchant matching\nend","preventionTips":["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."],"tags":["openai","responses-api","llm","empty-response","merchant-detection"],"backgroundTag":"llm-empty-response","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}