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::BankStatementExtractor#extract when extract_pages_from_pdf yields zero pages. That helper returns [] when pdf_content is blank, when PDF::Reader raises (malformed/encrypted PDF — the rescue swallows the exception into a log line 'Failed to extract text from PDF'), or when every page's text is blank after reject(&:blank?). The dominant real-world cause is a scanned/image-only statement with no text layer, which pdf-reader cannot read.
Source
Thrown at app/models/provider/openai/bank_statement_extractor.rb:13
class Provider::Openai::BankStatementExtractor
MAX_CHARS_PER_CHUNK = 3000
attr_reader :client, :pdf_content, :model
def initialize(client:, pdf_content:, model:)
@client = client
@pdf_content = pdf_content
@model = model
end
def extract
pages = extract_pages_from_pdf
raise Provider::Openai::Error, "Could not extract text from PDF" if pages.empty?
chunks = build_chunks(pages)
Rails.logger.info("BankStatementExtractor: Processing #{chunks.size} chunk(s) from #{pages.size} page(s)")
all_transactions = []
metadata = {}
chunks.each_with_index do |chunk, index|
Rails.logger.info("BankStatementExtractor: Processing chunk #{index + 1}/#{chunks.size}")
result = process_chunk(chunk, index == 0)
# Tag transactions with chunk index for deduplication
tagged_transactions = (result[:transactions] || []).map { |t| t.merge(chunk_index: index) }
all_transactions.concat(tagged_transactions)
if index == 0
metadata = {
account_holder: result[:account_holder],View on GitHub (pinned to e69894adb9)
Solutions
- Check the log for the swallowed exception ('Failed to extract text from PDF: ...') — encrypted/malformed PDFs identify themselves there.
- If the PDF is scanned, use a vision-based path (Provider::Openai::PdfProcessor#process_with_vision converts pages to images) instead of text extraction.
- Validate the upload before processing: non-empty bytes, %PDF- magic header, and page.text present on at least one page.
- For encrypted statements, decrypt with the user's password via a PDF library before extraction.
Example fix
# before pages = extract_pages_from_pdf raise Provider::Openai::Error, "Could not extract text from PDF" if pages.empty? # after pages = extract_pages_from_pdf if pages.empty? raise Provider::Openai::Error, "Could not extract text from PDF (likely scanned or encrypted)" unless vision_capable? extract_with_vision # fall back to page-image processing end
Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, "upload is empty" if pdf_content.to_s.empty?
raise ArgumentError, "not a PDF" unless pdf_content.start_with?("%PDF-") Type guard
def text_extractable_pdf?(bytes)
return false if bytes.blank?
reader = PDF::Reader.new(StringIO.new(bytes))
reader.pages.any? { |p| p.text.to_s.strip.present? }
rescue StandardError
false
end Try / catch
begin
extractor.extract
rescue Provider::Openai::Error => e
raise unless e.message.include?("Could not extract text")
render_error(:scanned_or_encrypted_pdf)
end Prevention
- Pre-check that at least one page has a text layer; route image-only PDFs to the vision path.
- Reject encrypted uploads early and prompt for the password instead of failing deep in extraction.
- Check the log line 'Failed to extract text from PDF' — it carries the real PDF::Reader exception.
When it happens
Trigger: Uploading a scanned bank statement (photos of pages, no OCR layer); a password-protected/encrypted PDF that PDF::Reader cannot open; a corrupt or zero-byte upload; a PDF whose pages contain only images; pdf_content passed as nil/empty from an upstream download failure.
Common situations: Bank exports that are image-based scans; users re-saving statements through scanner apps; mobile uploads of photographed statements; test fixtures that are placeholder bytes; encrypted e-statement PDFs (many Indian banks) whose password was never supplied.
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/282fffa25770e20d.
Report an issue: GitHub.