we-promise/sure · error · Provider::Anthropic::Error
Model did not invoke #{TOOL_NAME}
Error message
Model did not invoke #{TOOL_NAME} What it means
BankStatementExtractor#extract_tool_input scans the Messages response content for a :tool_use block; if none exists it raises "Model did not invoke report_bank_statement". The extractor forces the report_bank_statement tool so the statement structure comes back as JSON; a prose answer, a refusal ('do not invent values' pushed too far), or a response truncated by max_tokens before the tool call leaves the block missing.
Source
Thrown at app/models/provider/anthropic/bank_statement_extractor.rb:151
<<~INSTRUCTIONS
Extract bank statement data from the attached PDF and return the result via the report_bank_statement tool.
Rules:
- Extract EVERY transaction in document order
- Negative amounts for debits / expenses, positive for credits / deposits
- Dates in YYYY-MM-DD
- Use null for any field you cannot read; do not invent values
INSTRUCTIONS
end
def stop_reason(response)
raw = response.respond_to?(:stop_reason) ? response.stop_reason : nil
raw.to_s.to_sym if raw
end
def extract_tool_input(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)
input
end
def build_result(parsed)
# Intentionally NOT deduplicated, unlike Provider::Openai's extractor. That
# one chunks the PDF text with overlap and must drop transactions repeated
# across adjacent chunks. We send the whole PDF as a single native document
# block — no chunk artifacts — so deduping here would wrongly merge
# legitimate same-day, same-amount rows (e.g. two identical purchases).
# Preserve every transaction the model returns.
transactions = Array(parsed["transactions"] || parsed[:transactions]).map { |t| normalize_transaction(t) }.compact
{
transactions: transactions,
period: {View on GitHub (pinned to e69894adb9)
Solutions
- Retry the extraction once — tool-less responses are frequently one-off.
- Check the Langfuse span (extract_bank_statement_api_call) for stop_reason: if max_tokens, raise max_tokens or split the PDF into smaller documents.
- Use a current strong model (claude-sonnet-4-6 class) that reliably honors forced tool_choice.
- Verify custom-endpoint proxies forward tool definitions and tool_choice.
Example fix
# before
result = extractor.extract
# after
begin
result = extractor.extract
rescue Provider::Anthropic::Error => e
retry if (e.message.include?("did not invoke") && (attempts += 1) < 2)
raise
end Defensive patterns
Strategy: retry
Try / catch
attempts = 0
begin
result = extractor.extract
rescue Provider::Anthropic::Error => e
attempts += 1
retry if attempts < 2 && e.message.include?("did not invoke")
raise
end Prevention
- Watch stop_reason in the Langfuse span; max_tokens truncation needs a bigger cap or a smaller PDF, not retries.
- Split large statements so tool arguments fit the output budget.
- Use a current strong model for forced tool calls.
When it happens
Trigger: The model narrates the statement in text instead of calling the tool; max_tokens cuts the response (large statements produce large tool arguments); a custom proxy drops tool_choice; degraded image-only PDFs make the model hedge with prose.
Common situations: Huge statements whose tool payload exceeds the output cap; weak/legacy model behind a custom endpoint; proxy translation layer losing tool parameters; transient model behavior fixed by a retry.
Related errors
- Model did not invoke #{TOOL_NAME}
- Model did not invoke #{TOOL_NAME}
- Model did not invoke #{TOOL_NAME}
- PDF content is required
- PDF exceeds Anthropic's 32 MB limit (#{pdf_content.bytesize}
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/376d88be71aa17e7.
Report an issue: GitHub.