we-promise/sure · error · Provider::Anthropic::Error
Model did not invoke #{TOOL_NAME}
Error message
Model did not invoke #{TOOL_NAME} What it means
PdfProcessor#extract_tool_input scans the response for a :tool_use block and raises "Model did not invoke report_document_analysis" when none is found. The processor forces the report_document_analysis tool to get structured summary/document_type/extracted_data JSON back; a plain-text analysis, a refusal ('return null if unclear' over-applied), or a max_tokens truncation before the tool block makes extraction impossible.
Source
Thrown at app/models/provider/anthropic/pdf_processor.rb:146
Classification options:
- bank_statement: bank account statements (incl. mobile money / digital wallets)
- credit_card_statement: credit card statements
- investment_statement: brokerage / investment statements
- financial_document: tax forms, receipts, invoices, financial reports
- contract: legal agreements, loans, terms of service
- other: anything else
Rules:
- Be factual; only report what is clearly visible
- If a field is unclear/redacted, return null for it
- Do not invent figures or names you cannot read
- For statements with many transactions, return the count rather than enumerating them
INSTRUCTIONS
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)
PdfProcessingResult.new(
summary: parsed["summary"] || parsed[:summary],
document_type: normalize_document_type(parsed["document_type"] || parsed[:document_type]),
extracted_data: parsed["extracted_data"] || parsed[:extracted_data] || {}
)
end
def normalize_document_type(doc_type)
return "other" if doc_type.blank?
normalized = doc_type.to_s.strip.downcase.gsub(/\s+/, "_")View on GitHub (pinned to e69894adb9)
Solutions
- Retry once — the cheapest fix for intermittent tool-less responses.
- Inspect the process_pdf_api_call Langfuse span for stop_reason; if max_tokens, raise the cap or send fewer pages per request.
- Default to a current strong model (claude-sonnet-4-6) and confirm supports_pdf_processing? passed for it.
- Ensure any custom base_url proxy preserves tool definitions and tool_choice.
Example fix
# before
result = processor.process
# after
begin
result = processor.process
rescue Provider::Anthropic::Error => e
retry if e.message.include?("did not invoke") && (tries += 1) < 2
raise
end Defensive patterns
Strategy: retry
Try / catch
attempts = 0
begin
result = processor.process
rescue Provider::Anthropic::Error => e
attempts += 1
retry if attempts < 2 && e.message.include?("did not invoke")
raise
end Prevention
- Raise max_tokens or shrink the PDF when traces show stop_reason=max_tokens.
- Confirm gate supports_pdf_processing? passed for the configured model.
- Retry once — most tool-less responses are one-off.
When it happens
Trigger: The model describes the document in prose instead of calling the tool; the tool arguments for a dense statement exceed max_tokens so the block never completes; a custom proxy silently drops tool_choice; image-only or garbled PDFs cause the model to hedge in text.
Common situations: Long statements producing huge tool payloads; weak model chosen via settings; gateway translating Anthropic tool calls imperfectly; sporadic provider behavior that a single retry clears.
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 is too large (#{pdf_content.bytesize} bytes); base64-enc
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/b06bcdd77a97e758.
Report an issue: GitHub.