{"record":{"id":"91799336681ac53d","repo":"we-promise/sure","slug":"model-did-not-invoke-tool-name-917993","errorCode":null,"errorMessage":"Model did not invoke #{TOOL_NAME}","messagePattern":"Model did not invoke #(.+?)","errorType":"exception","errorClass":"Provider::Anthropic::Error","httpStatus":null,"severity":"error","filePath":"app/models/provider/anthropic/auto_merchant_detector.rb","lineNumber":135,"sourceCode":"    def user_message\n      <<~MESSAGE.strip_heredoc\n        User's known merchants:\n\n        ```json\n        #{user_merchants.to_json}\n        ```\n\n        Transactions to analyze:\n\n        ```json\n        #{transactions.to_json}\n        ```\n      MESSAGE\n    end\n\n    def extract_merchants(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      merchants = input.is_a?(Hash) ? (input[\"merchants\"] || input[:merchants]) : nil\n\n      raise Provider::Anthropic::Error, \"Tool call missing merchants\" unless merchants.is_a?(Array)\n      merchants\n    end\n\n    def build_response(merchants)\n      merchants.map do |m|\n        AutoDetectedMerchant.new(\n          transaction_id: m[\"transaction_id\"] || m[:transaction_id],\n          business_name: normalize_merchant_name(m[\"business_name\"] || m[:business_name]),\n          business_url: normalize_value(m[\"business_url\"] || m[:business_url])\n        )\n      end\n    end","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/anthropic/auto_merchant_detector.rb#L117-L153","documentation":"AutoMerchantDetector#extract_merchants scans the response for a :tool_use block; if none is present it raises \"Model did not invoke report_merchants\". The detector depends on Anthropic tool calling with the report_merchants tool forced, so a prose answer, an early truncation (stop_reason max_tokens), or a refusal leaves the expected tool block absent and the result cannot be extracted.","triggerScenarios":"The model answers in text instead of calling the tool; output is truncated before the tool_use block on very long merchant-name inputs; a custom endpoint/proxy strips tool_choice; adversarial or messy transaction memo text pushes the model off-format.","commonSituations":"Weaker model selected via settings; large batches near the 25 cap consuming the output budget; OpenAI-compatible bridge dropping Anthropic tool parameters; occasional provider flakiness that succeeds on retry.","solutions":["Retry once — the usual remedy for a one-off non-tool response.","Confirm the request forces tool_choice for report_merchants and inspect the Langfuse trace (anthropic.auto_detect_merchants) for the raw response.","Move to a current strong default model and/or shrink the batch to leave room for the tool call within max_tokens.","Validate that any custom base_url proxy forwards tool definitions and tool_choice untouched."],"exampleFix":"# before\nresult = detector.auto_detect_merchants\nrescue => e # generic, loses specificity\n\n# after\nbegin\n  result = detector.auto_detect_merchants\nrescue Provider::Anthropic::Error => e\n  retry if e.message.include?(\"did not invoke\") && (retries += 1) < 2\nend","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"attempts = 0\nbegin\n  result = provider.auto_detect_merchants(transactions: batch, family: family)\nrescue Provider::Anthropic::Error => e\n  attempts += 1\n  retry if attempts < 2\n  raise\nend","preventionTips":["Verify tool_choice forcing reaches the API when using custom base_url proxies.","Trim noisy memo text from prompts; adversarial payee names can derail tool use.","Retry once before alerting — most occurrences are transient."],"tags":["anthropic","llm","tool-use","merchant-detection","response-parsing"],"backgroundTag":"llm-tool-call-failure","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}