we-promise/sure · error · Provider::Openai::Error
Could not extract text from PDF
Error message
Could not extract text from PDF
What it means
Raised by Provider::Openai::PdfProcessor#process_with_text_extraction when extract_text_from_pdf returns nil/empty. The helper constructs a PDF::Reader over the bytes, joins each page's text with page markers, and rescues every exception to nil (logging 'Failed to extract text from PDF'). Blank therefore means one of: pdf_content blank, reader raised (encrypted/corrupt/unsupported PDF), or every page.text came back empty — the classic scanned-PDF signature. Text over ~100k chars is truncated afterward, so length is not a trigger.
Source
Thrown at app/models/provider/openai/pdf_processor.rb:101
"opening_balance": number or null,
"closing_balance": number or null,
"currency": "USD/EUR/etc or null",
"account_holder": "Name or null"
}
}
INSTRUCTIONS
end
private
PdfProcessingResult = Provider::LlmConcept::PdfProcessingResult
def process_with_text_extraction
effective_model = model.presence || Provider::Openai::DEFAULT_MODEL
# Extract text from PDF using pdf-reader gem
pdf_text = extract_text_from_pdf
raise Provider::Openai::Error, "Could not extract text from PDF" if pdf_text.blank?
# Truncate if too long (max ~100k chars to stay within token limits)
pdf_text = pdf_text.truncate(100_000) if pdf_text.length > 100_000
params = {
model: effective_model,
messages: [
{ role: "system", content: instructions },
{
role: "user",
content: "Please analyze the following document text and provide a structured summary:\n\n#{pdf_text}"
}
],
response_format: { type: "json_object" }
}
response = client.chat(parameters: params)
View on GitHub (pinned to e69894adb9)
Solutions
- Check the error log for the swallowed exception text to distinguish encrypted (password required) from corrupt (malformed PDF) from empty pages.
- Route scanned PDFs to process_with_vision, which rasterizes pages via pdftoppm instead of reading text.
- Pre-validate uploads: %PDF- header, non-trivial size, and at least one page with extractable text before choosing the text path.
- For encrypted files, collect the password and decrypt first (e.g. with hexapdf/qpdf) before extraction.
Example fix
# before
pdf_text = extract_text_from_pdf
raise Provider::Openai::Error, "Could not extract text from PDF" if pdf_text.blank?
# after
pdf_text = extract_text_from_pdf
if pdf_text.blank?
Rails.logger.info("PDF has no text layer; falling back to vision processing")
return process_with_vision
end Defensive patterns
Strategy: fallback
Try / catch
begin
processor.process
rescue Provider::Openai::Error => e
raise unless e.message.include?("Could not extract text")
processor.process_with_vision # same processor, image path handles scanned pages
end Prevention
- Detect the no-text-layer case up front and choose the vision path directly instead of waiting for the raise.
- Read the swallowed 'Failed to extract text from PDF' log to separate encrypted from corrupt files.
- Decrypt password-protected PDFs before handing bytes to the text extractor.
When it happens
Trigger: Image-only scanned PDF fed to the text path; password-protected PDF throwing inside PDF::Reader; corrupt/truncated upload bytes; malformed xref tables from certain PDF generators; nil pdf_content passed through from an upload handler.
Common situations: Users uploading phone-scanned documents; e-statements exported with DRM/encryption flags; PDFs produced by niche tools with quirky internal structure; the vision fallback not selected because the routing logic assumed text would exist.
Related errors
- Could not extract text from PDF
- Model does not support PDF/vision processing: #{effective_mo
- Could not convert PDF to images
- 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/f3bf5fd614eb2c27.
Report an issue: GitHub.