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

Model does not support PDF/vision processing: #{effective_mo

Error message

Model does not support PDF/vision processing: #{effective_model}

What it means

Raised by Provider::Openai#process_pdf when the effective model is not considered vision/PDF-capable. supports_pdf_processing? short-circuits to false when ENV OPENAI_SUPPORTS_PDF_PROCESSING is not one of true/1/yes (default is 'true'), returns true immediately for custom providers (uri_base set), and otherwise requires the model to start with one of VISION_CAPABLE_MODEL_PREFIXES: gpt-4o, gpt-4-turbo, gpt-4.1, gpt-5, o1, o3. The check runs against the effective model (the model: argument or @default_model), so a globally configured gpt-3.5-turbo or llama-style name on first-party OpenAI trips it.

Source

Thrown at app/models/provider/openai.rb:216

    end
  end

  # Can be disabled via ENV for OpenAI-compatible endpoints that don't support vision
  # Only vision-capable models (gpt-4o, gpt-4-turbo, gpt-4.1, etc.) support PDF input
  def supports_pdf_processing?(model: @default_model)
    return false unless ENV.fetch("OPENAI_SUPPORTS_PDF_PROCESSING", "true").to_s.downcase.in?(%w[true 1 yes])

    # Custom providers manage their own model capabilities
    return true if custom_provider?

    # Check if the specified model supports vision/PDF input
    VISION_CAPABLE_MODEL_PREFIXES.any? { |prefix| model.start_with?(prefix) }
  end

  def process_pdf(pdf_content:, model: "", family: nil)
    with_provider_response do
      effective_model = model.presence || @default_model
      raise Error, "Model does not support PDF/vision processing: #{effective_model}" unless supports_pdf_processing?(model: effective_model)

      trace = create_langfuse_trace(
        name: "openai.process_pdf",
        input: { pdf_size: pdf_content&.bytesize }
      )

      result = PdfProcessor.new(
        client,
        model: effective_model,
        pdf_content: pdf_content,
        custom_provider: custom_provider?,
        langfuse_trace: trace,
        family: family,
        max_response_tokens: max_response_tokens
      ).process

      upsert_langfuse_trace(trace: trace, output: result.to_h)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Set the model to a vision-capable one for PDF work: model: "gpt-4.1" (or gpt-4o/gpt-5/o-series) when calling process_pdf.
  2. Update OPENAI_MODEL / Setting.openai_model if the default itself is legacy.
  3. If the flag was disabled, set OPENAI_SUPPORTS_PDF_PROCESSING=true (or remove it to accept the default).
  4. For genuinely new OpenAI model families, add the prefix to VISION_CAPABLE_MODEL_PREFIXES in app/models/provider/openai.rb:9.

Example fix

# before
provider.process_pdf(pdf_content: pdf, model: "gpt-3.5-turbo")

# after
provider.process_pdf(pdf_content: pdf, model: "gpt-4.1")
Defensive patterns

Strategy: validation

Validate before calling

return unless provider.supports_model?(model)
return unless provider.supports_pdf_processing?(model: model) # cheap pre-check before uploading bytes

Type guard

def vision_capable_model?(model)
  %w[gpt-4o gpt-4-turbo gpt-4.1 gpt-5 o1 o3].any? { |p| model.to_s.start_with?(p) }
end

Try / catch

begin
  provider.process_pdf(pdf_content: bytes, model: model)
rescue Provider::Openai::Error => e
  return redirect_to(root_path, alert: "Model cannot read PDFs; pick a vision-capable model") if e.message.start_with?("Model does not support")
  raise
end

Prevention

When it happens

Trigger: Setting.openai_model or OPENAI_MODEL set to an older model (gpt-3.5-turbo, gpt-4 non-turbo) and uploading a statement PDF; passing model: "deepseek-r1" without a custom uri_base; OPENAI_SUPPORTS_PDF_PROCESSING=false in the environment disabling PDF processing for everyone including custom providers.

Common situations: Org pins a cheap legacy model for categorization and PDF uploads then start failing; the feature flag env is flipped off during a cost review and later forgotten; new model names shipping after this prefix list was written (e.g. a future gpt-6) fail the prefix check until the list is updated.

Related errors


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