we-promise/sure · error · Provider::Openai::Error
Could not convert PDF to images
Error message
Could not convert PDF to images
What it means
Raised by Provider::Openai::PdfProcessor#process_with_vision when convert_pdf_to_images returns an empty array. That helper writes the bytes to a temp file, shells out via system("pdftoppm", "-png", "-r", "150", ...) — poppler-utils — and globs page-*.png; every exception is rescued to [] and system failures (nonzero exit, command not found) simply leave no files. So the two root causes are pdftoppm missing from PATH (typical slim Docker images) and pdftoppm failing on the PDF itself (encrypted, corrupt, or unsupported).
Source
Thrown at app/models/provider/openai/pdf_processor.rb:154
text_parts = []
reader.pages.each_with_index do |page, index|
text_parts << "--- Page #{index + 1} ---"
text_parts << page.text
end
text_parts.join("\n\n")
rescue => e
Rails.logger.error("Failed to extract text from PDF: #{e.message}")
nil
end
def process_with_vision
effective_model = model.presence || Provider::Openai::DEFAULT_MODEL
# Convert PDF to images using pdftoppm
images_base64 = convert_pdf_to_images
raise Provider::Openai::Error, "Could not convert PDF to images" if images_base64.blank?
# Build message content with images (max 5 pages to avoid token limits)
content = []
images_base64.first(5).each do |img_base64|
content << {
type: "image_url",
image_url: {
url: "data:image/png;base64,#{img_base64}",
detail: "low"
}
}
end
content << {
type: "text",
text: "Please analyze this PDF document (#{images_base64.size} pages total, showing first #{[ images_base64.size, 5 ].min}) and respond with valid JSON only."
}
# Note: response_format is not compatible with vision, so we ask for JSON in the promptView on GitHub (pinned to e69894adb9)
Solutions
- Verify pdftoppm exists in the runtime image: docker run --rm <image> which pdftoppm; add poppler-utils (Debian/Ubuntu: apt-get install -y poppler-utils; Alpine: apk add poppler-utils).
- Capture pdftoppm's exit status and stderr in convert_pdf_to_images instead of discarding them, so missing-binary vs bad-PDF is distinguishable.
- Fail fast at boot/health check: raise a clear configuration error if system('which', 'pdftoppm') fails.
- For encrypted PDFs, decrypt before the vision path.
Example fix
# before
system("pdftoppm", "-png", "-r", "150", pdf_path, output_prefix)
image_files = Dir.glob(File.join(tmpdir, "page-*.png")).sort
# after
ok = system("pdftoppm", "-png", "-r", "150", pdf_path, output_prefix, err: "/tmp/pdftoppm.err")
Rails.logger.error("pdftoppm failed (#{$?.exitstatus}): #{File.read('/tmp/pdftoppm.err')}") unless ok
image_files = Dir.glob(File.join(tmpdir, "page-*.png")).sort Defensive patterns
Strategy: validation
Validate before calling
raise RuntimeError, "pdftoppm (poppler-utils) is not installed" unless system("which", "pdftoppm", out: File::NULL, err: File::NULL) Try / catch
begin
processor.process
rescue Provider::Openai::Error => e
raise unless e.message.include?("Could not convert PDF to images")
notify_ops("vision PDF path broken: check poppler-utils in the runtime image")
end Prevention
- Install poppler-utils in every runtime image (dev, CI, production) and assert its presence at boot.
- Capture pdftoppm exit status and stderr instead of discarding them, so missing binary vs bad PDF is obvious.
- Add a smoke test that converts a one-page fixture PDF through the vision path in CI.
When it happens
Trigger: Deploying to alpine/slim Docker without installing poppler-utils (system returns nil, zero PNGs); pdftoppm erroring 'Incorrect password' on an encrypted PDF; malformed PDF making pdftoppm exit nonzero; pdf_content blank hitting the early return [].
Common situations: Works on the dev Mac (poppler preinstalled via brew), fails in production container; CI pipeline lacking system packages; encrypted statements reaching the vision fallback; disk-full temp dirs making writes fail silently inside the rescue.
Related errors
- Model does not support PDF/vision processing: #{effective_mo
- Could not extract text from PDF
- Could not extract text from PDF
- Could not parse JSON from PDF processing response: #{raw.tru
- {e.record.errors.full_messages.to_sentence.presence || e.mes
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/b32f629cb0a94ed6.
Report an issue: GitHub.