we-promise/sure · error · Provider::Anthropic::Error
Tool call missing merchants
Error message
Tool call missing merchants
What it means
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.
Source
Thrown at app/models/provider/anthropic/provider_merchant_enhancer.rb:117
def user_message
<<~MESSAGE.strip_heredoc
Enhance the following merchants by identifying each one's website URL:
```json
#{merchants.to_json}
```
MESSAGE
end
def extract_enhancements(response)
tool_use = Array(response.content).find { |block| block_type(block) == :tool_use }
raise Provider::Anthropic::Error, "Model did not invoke #{TOOL_NAME}" unless tool_use
input = block_input(tool_use)
input = JSON.parse(input) if input.is_a?(String)
enhanced = input.is_a?(Hash) ? (input["merchants"] || input[:merchants]) : nil
raise Provider::Anthropic::Error, "Tool call missing merchants" unless enhanced.is_a?(Array)
enhanced
end
def build_response(enhanced)
enhanced.map do |m|
EnhancedMerchant.new(
merchant_id: m["merchant_id"] || m[:merchant_id],
business_url: normalize_value(m["business_url"] || m[:business_url])
)
end
end
def normalize_value(value)
return nil if value.nil?
str = value.to_s.strip
return nil if str.empty? || str.casecmp("null").zero?
str
endView on GitHub (pinned to e69894adb9)
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.
Example fix
# tool schema — before
{ type: "object", properties: { merchants: { type: "array" } } }
# model sends { "results": [...] } => "Tool call missing merchants"
# after
{ type: "object", required: ["merchants"],
properties: { merchants: { type: "array", items: { type: "object",
properties: { merchant_id: { type: ["string","integer"] }, business_url: { type: ["string","null"] } } } } } } Defensive patterns
Strategy: retry
Try / catch
attempts = 0
begin
result = enhancer.enhance
rescue Provider::Anthropic::Error => e
attempts += 1
retry if attempts < 2 && e.message.include?("missing merchants")
raise
end Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Tool call missing categorizations
- Tool call missing merchants
- Model did not invoke #{TOOL_NAME}
- Too many merchants to enhance. Max is 25 per request.
- Model did not invoke #{TOOL_NAME}
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/92bc5f7fa3fa0a7d.
Report an issue: GitHub.