{"record":{"id":"92bc5f7fa3fa0a7d","repo":"we-promise/sure","slug":"tool-call-missing-merchants-92bc5f","errorCode":null,"errorMessage":"Tool call missing merchants","messagePattern":"Tool call missing merchants","errorType":"exception","errorClass":"Provider::Anthropic::Error","httpStatus":null,"severity":"error","filePath":"app/models/provider/anthropic/provider_merchant_enhancer.rb","lineNumber":117,"sourceCode":"    def user_message\n      <<~MESSAGE.strip_heredoc\n        Enhance the following merchants by identifying each one's website URL:\n\n        ```json\n        #{merchants.to_json}\n        ```\n      MESSAGE\n    end\n\n    def extract_enhancements(response)\n      tool_use = Array(response.content).find { |block| block_type(block) == :tool_use }\n      raise Provider::Anthropic::Error, \"Model did not invoke #{TOOL_NAME}\" unless tool_use\n\n      input = block_input(tool_use)\n      input = JSON.parse(input) if input.is_a?(String)\n      enhanced = input.is_a?(Hash) ? (input[\"merchants\"] || input[:merchants]) : nil\n\n      raise Provider::Anthropic::Error, \"Tool call missing merchants\" unless enhanced.is_a?(Array)\n      enhanced\n    end\n\n    def build_response(enhanced)\n      enhanced.map do |m|\n        EnhancedMerchant.new(\n          merchant_id: m[\"merchant_id\"] || m[:merchant_id],\n          business_url: normalize_value(m[\"business_url\"] || m[:business_url])\n        )\n      end\n    end\n\n    def normalize_value(value)\n      return nil if value.nil?\n      str = value.to_s.strip\n      return nil if str.empty? || str.casecmp(\"null\").zero?\n      str\n    end","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/anthropic/provider_merchant_enhancer.rb#L99-L135","documentation":"The model did call report_enhancements, but the parsed tool input is not a Hash with a \"merchants\" Array (string inputs are JSON.parse'd; string and symbol keys accepted). \"Tool call missing merchants\" means the tool arguments have the wrong shape — a renamed key, a bare array, or truncated JSON — so the enhancement list cannot be built.","triggerScenarios":"The model returns {\"enhanced\": [...]} or a top-level array instead of {\"merchants\": [...]}; tool arguments get cut at max_tokens on larger batches and parse to an incomplete Hash; middleware on a proxy re-keys JSON tool input.","commonSituations":"Tool schema not marking \"merchants\" required, letting the model improvise key names; smaller/older models deviating; one-off malformed output; gateways that transform JSON keys.","solutions":["Retry — single malformed responses usually do not repeat.","Require \"merchants\" in the tool's input_schema with typed items so the model must use the exact key.","Switch to a current model and shrink the batch so tool arguments are not truncated.","Diff the raw tool input in the Langfuse trace against the schema to identify the renames."],"exampleFix":"# tool schema — before\n{ type: \"object\", properties: { merchants: { type: \"array\" } } }\n# model sends { \"results\": [...] } => \"Tool call missing merchants\"\n\n# after\n{ type: \"object\", required: [\"merchants\"],\n  properties: { merchants: { type: \"array\", items: { type: \"object\",\n    properties: { merchant_id: { type: [\"string\",\"integer\"] }, business_url: { type: [\"string\",\"null\"] } } } } } }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"attempts = 0\nbegin\n  result = enhancer.enhance\nrescue Provider::Anthropic::Error => e\n  attempts += 1\n  retry if attempts < 2 && e.message.include?(\"missing merchants\")\n  raise\nend","preventionTips":["Require \"merchants\" in the tool input schema with typed items.","Prefer current models; they follow tool schemas more reliably.","Inspect raw tool input in Langfuse when failures repeat — schema drift, not bad luck."],"tags":["anthropic","llm","tool-use","schema-mismatch","merchants"],"backgroundTag":"llm-malformed-tool-response","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}