{"record":{"id":"108a2dc4f29b01bd","repo":"MadsLorentzen/ai-job-search","slug":"text-layer-is-missing-required-text-required-r","errorCode":null,"errorMessage":"text layer is missing required text: {required!r} (extractor: {extractor})","messagePattern":"text layer is missing required text: (.+?) \\(extractor: (.+?)\\)","errorType":"exception","errorClass":"VerificationError","httpStatus":null,"severity":"error","filePath":"tools/verify_pdf.py","lineNumber":125,"sourceCode":"            raise VerificationError(\n                f\"could not write --dump-text to {dump_path}: {exc}\"\n            ) from exc\n\n    if expected_pages is not None and actual_pages != expected_pages:\n        raise VerificationError(\n            f\"expected {expected_pages} page(s), found {actual_pages} (extractor: {extractor})\"\n        )\n\n    normalized = normalize_text(extracted_text)\n    if len(normalized) < min_chars:\n        raise VerificationError(\n            f\"text layer has {len(normalized)} character(s); expected at least {min_chars} \"\n            f\"(extractor: {extractor})\"\n        )\n\n    for required in required_text:\n        if normalize_text(required) not in normalized:\n            raise VerificationError(\n                f\"text layer is missing required text: {required!r} (extractor: {extractor})\"\n            )\n    return extractor, extracted_text, actual_pages\n\n\ndef build_parser():\n    parser = argparse.ArgumentParser(\n        description=\"Verify a PDF's page count and ATS-readable text layer.\"\n    )\n    parser.add_argument(\"pdf\", type=Path, help=\"PDF file to verify\")\n    parser.add_argument(\"--pages\", type=int, help=\"required exact page count\")\n    parser.add_argument(\n        \"--min-chars\",\n        type=int,\n        default=1,\n        help=\"minimum non-whitespace text-layer characters (default: 1)\",\n    )\n    parser.add_argument(","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/MadsLorentzen/ai-job-search/blob/79cd383e58f0af7948c7c6462a3a289e9b67421e/tools/verify_pdf.py#L107-L143","documentation":"Raised when one of the required_text strings cannot be found in the PDF's normalized text layer. Both the extracted text and the required snippet are whitespace-normalized, so the failure means the wording genuinely differs, not merely line-break formatting.","triggerScenarios":"Calling verify_pdf(path, required_text=('Invoice Total',)) where that phrase (after whitespace collapsing) does not appear: dynamic content differs from the expected literal, unicode/ligature differences, hyphenation splitting words, or the phrase living in an image rather than the text layer.","commonSituations":"Template wording changed but the verification literal was not updated; localized date/currency formatting differences; ligatures (fi/fl) or smart quotes in extracted text; required text rendered inside an embedded image or SVG.","solutions":["Dump the extracted text (use dump_text/--dump-text or pdftotext) and search for the closest actual wording; update required_text to match","Fix the generator so the expected literal actually appears in the text layer","Normalize unicode on your side (e.g. unicodedata.normalize('NFKC') on both sides) or choose required snippets without ligatures/smart punctuation","If the phrase only exists as an image, add OCR or drop that required_text entry"],"exampleFix":"# before\nverify_pdf(pdf, required_text=('Total: 1,234.50',))\n# after — match actual localized formatting in the text layer\nverify_pdf(pdf, required_text=('Total:', '1.234,50'))","handlingStrategy":"validation","validationCode":"import subprocess\nnorm = lambda s: ' '.join(s.split())\ntext = norm(subprocess.run(['pdftotext', str(pdf), '-'], capture_output=True, text=True).stdout)\nmissing = [r for r in required if norm(r) not in text]\nassert not missing, f'will fail verification: {missing}'","typeGuard":null,"tryCatchPattern":"try:\n    verify_pdf(pdf, required_text=required)\nexcept VerificationError as e:\n    if 'missing required text' in str(e):\n        dump_and_diff(pdf, required)  # inspect actual wording, then adjust","preventionTips":["Keep required_text snippets short and punctuation-free to dodge unicode differences","Apply NFKC normalization to snippets and expected text on your side","Use dump_text to review exactly what the extractor sees before adding assertions"],"tags":["pdf","text-matching","verification","required-text","unicode"],"backgroundTag":null,"analyzedSha":"79cd383e58f0af7948c7c6462a3a289e9b67421e","analyzedAt":"2026-08-27T21:51:12.330Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}