{"record":{"id":"71516ca152b09ba5","repo":"spring-projects/spring-ai","slug":"unreasonable-number-of-lines-d-computed-from-co","errorCode":null,"errorMessage":"Unreasonable number of lines (%d) computed from content of pdf","messagePattern":"Unreasonable number of lines \\((.+?)\\) computed from content of pdf","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"document-readers/spring-ai-pdf-document-reader/src/main/java/org/springframework/ai/reader/pdf/layout/ForkPDFLayoutTextStripper.java","lineNumber":140,"sourceCode":"\t\telse {\n\t\t\tthis.addNewLine(); // white line\n\t\t}\n\t}\n\n\tprivate void iterateThroughTextList(Iterator<TextPosition> textIterator) {\n\t\tList<TextPosition> textPositionList = new ArrayList<>();\n\n\t\twhile (textIterator.hasNext()) {\n\t\t\tTextPosition textPosition = (TextPosition) textIterator.next();\n\t\t\tint numberOfNewLines = this.getNumberOfNewLinesFromPreviousTextPosition(textPosition);\n\t\t\tif (numberOfNewLines == 0) {\n\t\t\t\ttextPositionList.add(textPosition);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.writeTextPositionList(textPositionList);\n\t\t\t\tif (numberOfNewLines > 10_000) {\n\t\t\t\t\t// Throw rather than allocate crazy number of line objects\n\t\t\t\t\tthrow new IllegalStateException(\"Unreasonable number of lines (%d) computed from content of pdf\"\n\t\t\t\t\t\t.formatted(numberOfNewLines));\n\t\t\t\t}\n\t\t\t\tthis.createNewEmptyNewLines(numberOfNewLines);\n\t\t\t\ttextPositionList.add(textPosition);\n\t\t\t}\n\t\t\tthis.setPreviousTextPosition(textPosition);\n\t\t}\n\t\tif (!textPositionList.isEmpty()) {\n\t\t\tthis.writeTextPositionList(textPositionList);\n\t\t}\n\t}\n\n\tprivate void writeTextPositionList(final List<TextPosition> textPositionList) {\n\t\tthis.writeLine(textPositionList);\n\t\ttextPositionList.clear();\n\t}\n\n\tprivate void createNewEmptyNewLines(int numberOfNewLines) {","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/document-readers/spring-ai-pdf-document-reader/src/main/java/org/springframework/ai/reader/pdf/layout/ForkPDFLayoutTextStripper.java#L122-L158","documentation":"ForkPDFLayoutTextStripper.iterateThroughTextList() computes the number of new lines needed between text positions from PDF coordinates. If the computed numberOfNewLines exceeds 10,000, it throws IllegalStateException rather than allocating a huge number of empty line objects — protecting against pathological spacing values in the PDF content stream.","triggerScenarios":"Parsing a PDF (via PagePdfDocumentReader / layout text extraction) whose text positions imply a giant vertical gap — e.g. text positioned at extreme Y coordinates or a content stream with huge translation (Tm/Td) values — so the computed line gap exceeds 10,000.","commonSituations":"Corrupted or fuzzed PDFs; documents with off-page text positioning; adversarial PDFs designed to exhaust memory during layout-aware text extraction; files produced by generators that emit bogus transformation matrices.","solutions":["Validate/repair the PDF: open it in a viewer or run it through a normalizer (qpdf/gs) to fix extreme text positioning before extraction.","Extract text without layout mode (plain PDFTextStripper or PagePdfDocumentReader without layout) if exact layout is not required.","Catch IllegalStateException and skip/quarantine the document in batch processing.","If 10,000 lines is too low for legitimate documents, fork/raise the threshold in ForkPDFLayoutTextStripper."],"exampleFix":"// before\nDocumentReader reader = new PagePdfDocumentReader(resource, PagePdfDocumentReader.config().withLayoutEnabled());\nList<Document> docs = reader.get(); // throws on huge line gaps\n\n// after\nList<Document> docs;\ntry {\n    docs = new PagePdfDocumentReader(resource, PagePdfDocumentReader.config().withLayoutEnabled()).get();\n} catch (IllegalStateException e) {\n    docs = new PagePdfDocumentReader(resource).get(); // non-layout extraction\n}","handlingStrategy":"validation","validationCode":"try (PDDocument doc = Loader.loadPDF(file)) {\n    for (PDPage page : doc.getPages()) {\n        float h = page.getMediaBox().getHeight();\n        if (h > 14_400 || h <= 0)\n            throw new IllegalArgumentException(\"page height out of range: \" + h);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    List<Document> docs = new PagePdfDocumentReader(resource,\n        PagePdfDocumentReader.config().withLayoutEnabled()).get();\n} catch (IllegalStateException e) {\n    if (e.getMessage().startsWith(\"Unreasonable number of lines\")) {\n        docs = new PagePdfDocumentReader(resource).get(); // skip layout mode\n    } else throw e;\n}","preventionTips":["Only enable layout-aware extraction on PDFs with validated page geometry","Normalize corrupted PDFs with qpdf --clean or ghostscript before parsing","Disable layout mode when exact text positioning is not required","Quarantine documents that trigger the 10,000-line guard and inspect them manually"],"tags":["pdf","layout","memory-protection","document-reader"],"backgroundTag":"value-out-of-range","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}