we-promise/sure · error · Provider::Anthropic::Error
Model did not invoke #{TOOL_NAME}
Error message
Model did not invoke #{TOOL_NAME} What it means
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.
Source
Thrown at app/models/provider/anthropic/auto_merchant_detector.rb:135
def user_message
<<~MESSAGE.strip_heredoc
User's known merchants:
```json
#{user_merchants.to_json}
```
Transactions to analyze:
```json
#{transactions.to_json}
```
MESSAGE
end
def extract_merchants(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)
merchants = input.is_a?(Hash) ? (input["merchants"] || input[:merchants]) : nil
raise Provider::Anthropic::Error, "Tool call missing merchants" unless merchants.is_a?(Array)
merchants
end
def build_response(merchants)
merchants.map do |m|
AutoDetectedMerchant.new(
transaction_id: m["transaction_id"] || m[:transaction_id],
business_name: normalize_merchant_name(m["business_name"] || m[:business_name]),
business_url: normalize_value(m["business_url"] || m[:business_url])
)
end
endView on GitHub (pinned to e69894adb9)
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.
Example fix
# before
result = detector.auto_detect_merchants
rescue => e # generic, loses specificity
# after
begin
result = detector.auto_detect_merchants
rescue Provider::Anthropic::Error => e
retry if e.message.include?("did not invoke") && (retries += 1) < 2
end Defensive patterns
Strategy: retry
Try / catch
attempts = 0 begin result = provider.auto_detect_merchants(transactions: batch, family: family) rescue Provider::Anthropic::Error => e attempts += 1 retry if attempts < 2 raise end
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Model did not invoke #{TOOL_NAME}
- Tool call missing merchants
- Model did not invoke #{TOOL_NAME}
- Model did not invoke #{TOOL_NAME}
- Model did not invoke #{TOOL_NAME}
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/91799336681ac53d.
Report an issue: GitHub.