pdf-inspector Error Handling & Limits - Debugging Guide
Error HandlingLimits
First things first: pdf-inspector's philosophy is never fake success — when content can't be extracted it tells you which page, why, and what to do. This page covers the common failure modes and known limits in one place.
Failure modes at a glance
| Situation | What you see | What to do |
|---|---|---|
| Document is scanned/image-based | pdfType = Scanned/ImageBased, markdown empty or missing | Needs OCR: enable selective OCR natively or route to an external OCR service |
| Some pages need OCR | pagesNeedingOcr lists them (common for Mixed) | Send only those pages to OCR; extract the rest locally |
| Broken font encodings | Python has_encoding_issues=True; region extraction needsOcr=True (ocrReason="suspected_garbled_text") | Fall back to OCR for those pages — retrying won't help, the source is broken |
| PDF encrypted | Parse error | Decrypt / remove password protection first |
| PDF corrupt | Parse error | Re-export from the original application and retry |
| Browser parse fails | WASM throws | Verify it's a valid, unencrypted PDF; watch memory for huge files |
Known limits (important)
The browser WASM build has no OCR
Scanned/image-only documents yield a type verdict and a clear message in the browser. This is by design — the OCR runtime (PDFium + ONNX Runtime + models) doesn't fit a web page.
Workarounds:
- Use selective OCR on the server via the native packages (
processPdfWithOcr/process_pdf_with_ocr) - Or add a text layer with an external OCR first, then parse with WASM
Visual fidelity isn't preserved
Output is Markdown, not a PDF snapshot. Complex layouts (absolutely-positioned text boxes, word art, intricate diagrams) degrade to readable text — information completeness is backed by the 0.875 benchmark score, not pixel-perfect rendering.
Page indexing isn't uniform across bindings
Python's pages_needing_ocr is 1-indexed, while extract_pages_markdown results use 0-indexed pages; Node's pagesNeedingOcr is 0-indexed while processPdfWithOcr's pageNumbers are 1-indexed. Check each binding's docs before writing routing logic.
Sync APIs occupy the calling thread
Node's processPdf / classifyPdf run on the event loop. Servers should always use the *Async variants (libuv thread pool); browsers should move large files into a Web Worker.
Frequent questions
The output is empty?
Check the classification first: most likely the document isn't TextBased. Confirm with detect-pdf document.pdf --analyze --json. If it IS text-based but empty, check for encryption and verify the file opens elsewhere.
The extracted text is garbled?
Nine times out of ten the source file's font encoding was already broken (GID-encoded fonts, missing ToUnicode CMaps). pdf-inspector flags those pages (has_encoding_issues / needsOcr) — route them to OCR instead of retrying.
Reading order looks wrong?
pdf-inspector scores 0.915 (best in class) on reading order, but extreme layouts (complex nested columns, artistic typography) can still trip it. Compare against per-page mode (extractPagesMarkdown) output to isolate problem pages.
Parsing feels slow?
A text-based PDF normally finishes within 200ms end-to-end. If it's slow: lower sampling for large files (Rust ScanStrategy::Sample(n)), pass pages when you need only part of the document, and make sure you call wasm init() only once.
How do I verify correctness?
Spot-check three things: heading levels (# counts), multi-column ordering, and table regularity. For bulk ingestion, snapshot-test a few key documents.
In one sentence
Classify first, extract second, fall back to OCR when encodings break — remember that decision chain and pdf-inspector has few traps left.