Failed to read PDF: unexpected end of fileThis error means the PDF ended before the parser expected, often from truncated downloads or partial writes.
If a PDF is downloaded over an unreliable connection and the transfer is interrupted, the file on disk is incomplete. The PDF header may be valid but the cross-reference table and trailer section at the end of the file are missing, causing the parser to fail.
If the process writing a PDF is killed mid-way (OOM kill, power loss, server restart), the resulting file on disk is truncated. PDF structure requires a complete %%EOF trailer — without it, strict parsers reject the file entirely.
Some PDF generators produce technically invalid output: duplicate object IDs, malformed cross-reference tables, incorrect stream lengths, or invalid encoding. These files open in forgiving readers like Adobe Acrobat but fail in strict parsers that enforce the spec.
PDFluent's open_with_repair() attempts to reconstruct the cross-reference table by scanning the file byte-by-byte. This recovers most truncated or structurally broken PDFs without requiring the %%EOF trailer.
use pdfluent::PdfDocument;
// Attempt repair before giving up
let doc = Document::open_with_repair("damaged.pdf")?;
let page_count = doc.page_count();
println!("Recovered {} pages", page_count);If you process PDFs uploaded by users or received from external systems, validate them before passing to downstream processing. This avoids cascade failures and gives you a clean error to return to the caller.
use pdfluent::PdfDocument;
fn process_pdf(path: &str) -> anyhow::Result<()> {
// Check file integrity first
match Document::validate(path) {
Ok(report) if report.is_valid() => {
let doc = PdfDocument::open(path)?;
// safe to process
}
Ok(report) => {
eprintln!("PDF has warnings: {:?}", report.warnings());
// decide whether to attempt repair or reject
}
Err(e) => {
eprintln!("PDF is unreadable: {}", e);
return Err(e.into());
}
}
Ok(())
}A common production pattern is to attempt a normal open first, and fall back to repair mode only on failure. This keeps the fast path clean while handling corrupted files gracefully.
use pdfluent::PdfDocument;
fn open_robust(path: &str) -> anyhow::Result<Document> {
match PdfDocument::open(path) {
Ok(doc) => Ok(doc),
Err(_) => {
eprintln!("Normal open failed, attempting repair...");
Document::open_with_repair(path)
.map_err(|e| anyhow::anyhow!("Repair also failed: {}", e))
}
}
}