we-promise/sure · error · Provider::Anthropic::Error

Model did not invoke #{TOOL_NAME}

Error message

Model did not invoke #{TOOL_NAME}

What it means

ProviderMerchantEnhancer#extract_enhancements scans the response content for a :tool_use block; if none exists it raises "Model did not invoke report_enhancements". The enhancer forces the report_enhancements tool so merchant website URLs come back as structured JSON; a prose reply, a truncated response, or a refusal ('I cannot browse') leaves the tool block missing.

Source

Thrown at app/models/provider/anthropic/provider_merchant_enhancer.rb:111

        - Do NOT include the www. subdomain ("walmart.com", not "www.walmart.com")
        - Favor null over false positives; only return a URL when 80%+ confident
        - NEVER return a URL for generic or local-only merchants ("Local diner", "Gas station", "ATM withdrawal")
      INSTRUCTIONS
    end

    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

View on GitHub (pinned to e69894adb9)

Solutions

  1. Retry the enhancement call once.
  2. Verify tool_choice forcing for report_enhancements in the request via the enhance_provider_merchants Langfuse trace.
  3. Use a current strong model and reduce the merchant batch size if truncation is the cause.
  4. Confirm custom proxies forward tool definitions and tool_choice.

Example fix

# before
result = enhancer.enhance

# after
begin
  result = enhancer.enhance
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 = provider.enhance_provider_merchants(merchants: batch, family: family)
rescue Provider::Anthropic::Error => e
  attempts += 1
  retry if attempts < 2 && e.message.include?("did not invoke")
  raise
end

Prevention

When it happens

Trigger: enhance_provider_merchants runs and the model answers in text (e.g. listing URLs inline) instead of calling the tool; max_tokens truncation on large merchant batches; a custom endpoint proxy stripping tool_choice; the model refusing because merchant names look like PII.

Common situations: Weaker model selected in settings; batches near the 25 cap; gateway dropping tool parameters; transient model behavior that succeeds on retry.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/86945daeff0378b6. Report an issue: GitHub.