This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new fdef6a4435 TIKA-4911: PDF page OCR: an engine's TikaException no 
longer aborts the document (#3203)
fdef6a4435 is described below

commit fdef6a44359bdb250a743a3e4ca83985dea1e798
Author: Tim Allison <[email protected]>
AuthorDate: Fri Sep 18 16:04:57 2026 -0400

    TIKA-4911: PDF page OCR: an engine's TikaException no longer aborts the 
document (#3203)
---
 CHANGES.txt                                        |  2 +
 .../apache/tika/parser/pdf/AbstractPDF2XHTML.java  | 16 ++++++++
 .../org/apache/tika/parser/pdf/PDFParserTest.java  | 47 ++++++++++++++++++++++
 3 files changed, 65 insertions(+)

diff --git a/CHANGES.txt b/CHANGES.txt
index 8c5b58bb7d..7ed0b8a415 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,7 @@
 Release 4.1.0 - unreleased
 
+   * Handle inference exceptions more robustly in PDFParser (TIKA-4911).
+
    * Add ffmpeg to tika-server's "full" Docker image (TIKA-4910).
 
    * Enable environment variable interpolation in tika-config.json (TIKA-4909).
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/AbstractPDF2XHTML.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/AbstractPDF2XHTML.java
index bb3e9db5a4..7f1018c8a2 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/AbstractPDF2XHTML.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/AbstractPDF2XHTML.java
@@ -677,6 +677,20 @@ class AbstractPDF2XHTML extends PDFTextStripper {
         }
     }
 
+    /**
+     * An engine's own failure on one page (a remote OCR service answering 401 
or 500, a
+     * tesseract exit code): recorded like a timeout, the page keeps its text, 
the remaining
+     * pages still run, and the first failure surfaces once every page has 
been attempted.
+     */
+    void handleCatchableEngineFailure(TikaException e) throws TikaException {
+        if (config.isCatchIntermediateIOExceptions()) {
+            EmbeddedDocumentUtil.recordException(e, metadata, context);
+            exceptions.add(e);
+        } else {
+            throw e;
+        }
+    }
+
     /** What the OCR step did with a page. */
     enum PageOcr {
         /** No engine was dispatched: NO_OCR, no engine, or maxPagesToOcr 
spent. */
@@ -764,6 +778,8 @@ class AbstractPDF2XHTML extends PDFTextStripper {
             handleCatchableIOE(e);
         } catch (TikaTimeoutException e) {
             handleCatchableTimeout(e);
+        } catch (TikaException e) {
+            handleCatchableEngineFailure(e);
         } catch (SAXException e) {
             throw new IOException("error writing OCR content from PDF", e);
         }
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/java/org/apache/tika/parser/pdf/PDFParserTest.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/java/org/apache/tika/parser/pdf/PDFParserTest.java
index 949fcc7d9f..e48e93a0ae 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/java/org/apache/tika/parser/pdf/PDFParserTest.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/java/org/apache/tika/parser/pdf/PDFParserTest.java
@@ -1728,6 +1728,37 @@ public class PDFParserTest extends TikaTest {
         assertWellFormed(xml);
     }
 
+    /**
+     * An engine's TikaException (a remote OCR service answering 401) is 
handled like its
+     * IOException: recorded, the page keeps its text, later pages parse, the 
failure surfaces
+     * at the end. Before, it escaped as "Unable to end a page" and lost every 
page's text.
+     */
+    @Test
+    public void testAutoOcrEngineRefusalFallsBackToText() throws Exception {
+        PDFParserConfig config = autoOcrTriggeringPage16();
+        ParseContext context = new ParseContext();
+        context.set(PDFParserConfig.class, config);
+        context.set(Parser.class, refusingOcrParser(config));
+        Metadata metadata = new Metadata();
+        ToXMLContentHandler handler = new ToXMLContentHandler();
+        TikaException thrown = null;
+        try (TikaInputStream tis =
+                     
getResourceAsStream("/test-documents/testPDF_bad_page_303226.pdf")) {
+            new PDFParser().parse(tis, handler, metadata, context);
+        } catch (TikaException e) {
+            thrown = e;
+        }
+        String xml = handler.toString();
+
+        assertContains("42936", xml);
+        assertContains("ANABOLIC", xml);
+        assertContains("1308.44", xml);
+        assertContains("HTTP 401", String.join("\n",
+                
metadata.getValues(TikaCoreProperties.TIKA_META_EXCEPTION_WARNING)));
+        assertNotNull(thrown, "the recorded refusal still surfaces once every 
page is done");
+        assertWellFormed(xml);
+    }
+
     /** TIKA-4883: once maxPagesToOcr is exhausted, later triggering pages 
keep their text. */
     @Test
     public void testAutoOcrMaxPagesFallsBackToText() throws Exception {
@@ -2269,6 +2300,22 @@ public class PDFParserTest extends TikaTest {
         };
     }
 
+    /** A hosted engine refusing the request: the VLM parsers wrap HTTP errors 
this way. */
+    private static Parser refusingOcrParser(PDFParserConfig config) {
+        return new MockEngine() {
+            @Override
+            public Set<MediaType> getSupportedTypes(ParseContext context) {
+                return Collections.singleton(ocrMediaType(config));
+            }
+
+            @Override
+            public void parse(TikaInputStream tis, ContentHandler handler, 
Metadata metadata,
+                              ParseContext context) throws TikaException {
+                throw new TikaException("HTTP 401 from 
https://ocr.example/v1/chat/completions";);
+            }
+        };
+    }
+
     private static Parser failingOcrParser(PDFParserConfig config) {
         return new MockEngine() {
             @Override

Reply via email to