{"record":{"id":"11bbbfbd0c17e46b","repo":"firecrawl/pdf-inspector","slug":"merge-never-returns-ocr-only-content","errorCode":null,"errorMessage":"merge never returns OCR-only content","messagePattern":"merge never returns OCR-only content","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/vision/fusion.rs","lineNumber":510,"sourceCode":"            warning: format!(\n                \"kept higher-quality {} after comparing OCR\",\n                native.origin.description()\n            ),\n            recommend_hosted: false,\n        };\n    }\n\n    let (markdown, source) = merge_native_and_ocr(native.markdown(), ocr);\n    let warning = match source {\n        PageContentSource::Native => format!(\n            \"kept trustworthy {} because OCR duplicated its content\",\n            native.origin.description()\n        ),\n        PageContentSource::Fused => format!(\n            \"fused trustworthy {} with complementary OCR\",\n            native.origin.description()\n        ),\n        PageContentSource::Ocr => unreachable!(\"merge never returns OCR-only content\"),\n    };\n    AdaptiveContentChoice {\n        markdown,\n        source,\n        warning,\n        recommend_hosted: false,\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\nstruct ContentOverlap {\n    shared_chars: usize,\n}\n\nfn content_overlap(first: &str, second: &str) -> ContentOverlap {\n    let mut first_counts = BTreeMap::<char, usize>::new();\n    for character in normalized_content_chars(first) {\n        *first_counts.entry(character).or_insert(0) += 1;","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/firecrawl/pdf-inspector/blob/636ca1a58bdc1af4cd3fc20b8c1f549a1121cca7/src/vision/fusion.rs#L492-L528","documentation":"In choose_adaptive_content (src/vision/fusion.rs), merge_native_and_ocr is documented to only return Native or Fused; the PageContentSource::Ocr arm is an unreachable!('merge never returns OCR-only content'). Hitting it means the merge function violated its contract and returned OCR-only content — a library bug, not a user-triggerable condition. It aborts the thread with a panic rather than producing a catchable error.","triggerScenarios":"Only reachable if merge_native_and_ocr's internal logic is changed/buggy and returns PageContentSource::Ocr; cannot be triggered by any external input through documented APIs.","commonSituations":"Developers modifying merge_native_and_ocr (e.g. changing the overlap/novelty thresholds or returning Ocr for low-overlap pages) break the invariant; downstream maintainers hitting it after a refactor of the fusion module.","solutions":["Revert or fix the change to merge_native_and_ocr so it can only return Native or Fused.","If OCR-only output is now a legitimate outcome, replace the unreachable! with a real arm producing an Ocr warning instead of panicking.","Add a unit test asserting merge_native_and_ocr never yields PageContentSource::Ocr for representative inputs.","Reproduce with RUST_LOG=pdf_inspector::vision=debug and the failing PDF, then inspect the overlap/novelty values at the merge site."],"exampleFix":"// before\nPageContentSource::Ocr => unreachable!(\"merge never returns OCR-only content\"),\n// after\nPageContentSource::Ocr => format!(\n    \"kept OCR content for {} (no trustworthy native text)\",\n    native.origin.description()\n),","handlingStrategy":"type-guard","validationCode":"// Library-side guard: assert the merge contract before formatting the warning\nlet source = merge_native_and_ocr(native.markdown(), ocr).1;\ndebug_assert!(matches!(source, PageContentSource::Native | PageContentSource::Fused));","typeGuard":"fn merge_source_is_valid(source: PageContentSource) -> bool {\n    matches!(source, PageContentSource::Native | PageContentSource::Fused)\n}","tryCatchPattern":"// Not user-catchable (panic). Library-side: replace unreachable! with a graceful arm\nlet warning = match source {\n    PageContentSource::Ocr => {\n        log::warn!(\"merge returned OCR-only content; treating as fused\");\n        \"fused with OCR\".to_string()\n    }\n    other => /* existing arms */\n};","preventionTips":["Never change merge_native_and_ocr to return PageContentSource::Ocr without updating this match.","Add a unit test pinning the invariant that merge output is always Native or Fused.","Prefer explicit error/warning arms over unreachable! for values that could become reachable after refactors.","Run the vision fusion test suite after any change to overlap/novelty thresholds."],"tags":["invariant-violation","panic","internal-bug","ocr"],"backgroundTag":"unreachable-invariant-violated","analyzedSha":"636ca1a58bdc1af4cd3fc20b8c1f549a1121cca7","analyzedAt":"2026-09-05T08:40:31.256Z","contentChangedAt":"2026-09-05T08:40:31.256Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}