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

PDF content is required

Error message

PDF content is required

What it means

Provider::Anthropic::BankStatementExtractor#extract raises "PDF content is required" when pdf_content is blank (nil or empty string). The extractor sends the raw PDF bytes as a native document block to the Messages API, so empty content has nothing to send; the guard runs before the API call, the Langfuse span, and size check.

Source

Thrown at app/models/provider/anthropic/bank_statement_extractor.rb:20

  include Provider::Anthropic::Concerns::UsageRecorder

  TOOL_NAME = "report_bank_statement".freeze

  # Mirrors Provider::Anthropic::PdfProcessor::MAX_PDF_BYTES.
  MAX_PDF_BYTES = 32 * 1024 * 1024

  attr_reader :client, :model, :pdf_content, :langfuse_trace, :family

  def initialize(client:, model:, pdf_content:, langfuse_trace: nil, family: nil)
    @client = client
    @model = model
    @pdf_content = pdf_content
    @langfuse_trace = langfuse_trace
    @family = family
  end

  def extract
    raise Provider::Anthropic::Error, "PDF content is required" if pdf_content.blank?
    if pdf_content.bytesize > MAX_PDF_BYTES
      raise Provider::Anthropic::Error,
            "PDF exceeds Anthropic's 32 MB limit (#{pdf_content.bytesize} bytes)"
    end

    span = langfuse_trace&.span(name: "extract_bank_statement_api_call", input: {
      model: model,
      pdf_size: pdf_content.bytesize
    })

    response = client.messages.create(
      model: model,
      max_tokens: max_tokens,
      system_: instructions,
      messages: [ { role: "user", content: user_content } ],
      tools: [ output_tool ],
      tool_choice: { type: "tool", name: TOOL_NAME, disable_parallel_tool_use: true }
    )

View on GitHub (pinned to e69894adb9)

Solutions

  1. Fix the caller to pass actual binary content (pdf.download / File.binread(path)), not a path or File handle.
  2. Guard upstream: verify the uploaded statement has non-zero bytes before enqueuing the extraction job.
  3. If the attachment itself is empty, re-import/re-upload the statement.

Example fix

# before
extractor.extract # pdf_content came from statement.file.path (a String path)
# => PDF content is required

# after
extractor = BankStatementExtractor.new(client:, model:, pdf_content: statement.file.download)
extractor.extract
Defensive patterns

Strategy: validation

Validate before calling

pdf_bytes = statement.file.download
if pdf_bytes.blank?
  return error("Statement file is empty — re-upload")
end
extractor.extract # constructed with pdf_content: pdf_bytes

Try / catch

begin
  extractor.extract
rescue Provider::Anthropic::Error => e
  # input problem: re-upload, don't retry
  statement.mark_import_failed(reason: e.message)
end

Prevention

When it happens

Trigger: Extract is called with pdf_content: nil — an upload whose file read returned nil, a statement record with an empty attachment, or an upstream pipeline that passes the filename instead of contents.

Common situations: Controller/job reads the attachment before it finished uploading; tempfile closed and re-read returns nil; code passes pdf_path or File object where bytes are expected; statement import from a URL that downloaded zero bytes.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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