{"slug": "exit-codes-lie-when-pdf-extraction-yields-nothing", "title": "Exit codes lie when PDF extraction yields nothing", "summary": "A developer's integration with Claude Code and markitdown falsely reported a PDF as empty because exit codes only indicate process completion, not content yield, and byte thresholds miss near-misses like a 39-character certificate. The author proposes a two-layer check combining structural validation and a density heuristic of 50 characters per page, plus sample verification with a cheap model, to catch extraction failures in any pipeline.", "body_md": "# Exit codes lie when PDF extraction yields nothing\n\n[Claude Code](/en/tags/claude%20code/)cheerfully told me the document was empty. The PDF wasn't empty — it was a browser screenshot export with zero text layer. Every tool in the chain did exactly what it was designed to do, reported success, and the stacked successes produced a lie I believed.\n\n## The real failure point\n\nmarkitdown isn't broken. It extracts embedded text and deliberately doesn't OCR — documented design decision. `pdfminer`\n\nand PyMuPDF both report 0 characters. The converter exiting non-zero on empty documents would be wrong in a more annoying way.\n\nThe bug lives in every integration that treats exit code as evidence of yield. Exit code answers \"did the process complete?\" I read it as \"did we get the text?\" Different questions, different answers for scanned pages.\n\n``` bash\n$ markitdown screenshot.pdf -o out.md\n$ echo $?\n0\n$ wc -c out.md\n0 out.md\n```\n\nMy integration checked the return code, saw success, cached the result, handed the model a path to an empty file. The model read nothing and concluded the document was empty. Reasonable from its position.\n\n## Why byte thresholds fail\n\nThe obvious fix — reject anything under 500 bytes — catches the screenshot. It misses the case that actually cost me time: a course completion certificate. One decorative graphic, one title line as real text. Conversion yields:\n\n```\nCertificate of Completion\n```\n\nThirty-nine characters. Sails through emptiness checks, gets cached as successful, and the model reports your certificate says \"Certificate of Completion\" and nothing else.\n\nNear-misses are the expensive failures: slide decks exported as page images with footers, contracts scanned at an angle with headers that OCR'd once. They all return *some* characters.\n\n## What actually works\n\nI've settled on a two-layer check that catches both failure modes:\n\n**1. Structural validation** — does the output contain paragraph-like structures, not just isolated lines? A real document has sentences, line breaks, recurring patterns. A certificate has one line.\n\n**2. Density heuristic** — characters per page. If a 10-page PDF yields 200 characters total, extraction failed regardless of exit code. Threshold varies by domain (legal > technical > certificates), but 50 chars/page is a starting baseline.\n\n``` php\ndef extraction_quality(text: str, page_count: int) -> bool:\n    if len(text) < 50 * page_count:\n        return False\n    paragraphs = [p for p in text.split('\\n\\n') if len(p.strip()) > 20]\n    return len(paragraphs) >= max(2, page_count // 3)\n```\n\n**3. Sample verification** — feed the first 500 chars to a cheap model with a strict prompt: \"Does this look like meaningful document content or extraction artifacts?\" Costs pennies, catches the certificate case every time.\n\n## The pattern generalizes\n\nThis isn't PDF-specific. Any pipeline where a transformer can succeed while producing useless output has this shape: OCR, speech-to-text, HTML-to-markdown, code transpilers. The fix is always the same — measure yield, not completion.\n\nWhat's your threshold strategy? I've seen teams use word count, unique word ratio, even embedding distance from a \"garbage\" centroid. Curious what's worked in production.\n\n[Next Hard Gates Beat Long Prompts →](/en/threads/7064/)\n\n## All Replies （3）\n\n`pdftotext`\n\nreturns 0 but outputs nothing unless you force OCR first", "url": "https://wpnews.pro/news/exit-codes-lie-when-pdf-extraction-yields-nothing", "canonical_source": "https://promptcube3.com/en/threads/7065/", "published_at": "2026-08-20 16:12:58+00:00", "updated_at": "2026-08-20 16:44:37.454564+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence"], "entities": ["Claude Code", "markitdown", "pdfminer", "PyMuPDF"], "alternates": {"html": "https://wpnews.pro/news/exit-codes-lie-when-pdf-extraction-yields-nothing", "markdown": "https://wpnews.pro/news/exit-codes-lie-when-pdf-extraction-yields-nothing.md", "text": "https://wpnews.pro/news/exit-codes-lie-when-pdf-extraction-yields-nothing.txt", "jsonld": "https://wpnews.pro/news/exit-codes-lie-when-pdf-extraction-yields-nothing.jsonld"}}