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

PDF content is required

Error message

PDF content is required

What it means

Provider::Anthropic::PdfProcessor#process raises "PDF content is required" when pdf_content is blank. The processor base64-encodes the PDF into a document block for the Messages API, so nil/empty content cannot proceed; the guard fires before the Langfuse span and the size check.

Source

Thrown at app/models/provider/anthropic/pdf_processor.rb:28

  # minus a generous envelope reserve, so the encoded request stays under the
  # limit. Guarding upstream also avoids base64-encoding an over-size blob in
  # vain (peak heap before the API would reject it).
  MAX_REQUEST_BYTES = 32 * 1024 * 1024
  REQUEST_ENVELOPE_BYTES = 1 * 1024 * 1024
  MAX_PDF_BYTES = (MAX_REQUEST_BYTES - REQUEST_ENVELOPE_BYTES) * 3 / 4

  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 process
    raise Provider::Anthropic::Error, "PDF content is required" if pdf_content.blank?
    if pdf_content.bytesize > MAX_PDF_BYTES
      raise Provider::Anthropic::Error,
            "PDF is too large (#{pdf_content.bytesize} bytes); base64-encoded it would exceed Anthropic's 32 MB request limit"
    end

    span = langfuse_trace&.span(name: "process_pdf_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. Pass the real bytes: attachment.download or File.binread(path).
  2. Validate non-empty content where the upload is accepted (controller/model validation) so the job never gets blank input.
  3. If the source is empty, re-fetch/re-upload the document.

Example fix

# before
processor.process # pdf_content: doc.file_path (String) 
# => PDF content is required

# after
processor = PdfProcessor.new(client, model: model, pdf_content: doc.file.download)
processor.process
Defensive patterns

Strategy: validation

Validate before calling

content = doc.file.download
if content.blank?
  return error("Document is empty — re-upload")
end
provider.process_pdf(pdf_content: content, model: model)

Try / catch

begin
  processor.process
rescue Provider::Anthropic::Error => e
  doc.mark_failed(reason: e.message)
end

Prevention

When it happens

Trigger: process is called with pdf_content: nil or "" — an attachment read that returned nil, a document record with empty content, or a caller passing a path/URL string where the binary body is expected.

Common situations: Active Storage blob not yet uploaded when the job runs; tempfile lifetime issues; download from a URL returned an empty body; refactor changed the parameter from bytes to something else.

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/e889263f076e21d3. Report an issue: GitHub.