{"record":{"id":"b5c73e31c56fabd5","repo":"spring-projects/spring-ai","slug":"line-length-cannot-be-negative","errorCode":null,"errorMessage":"Line length cannot be negative","messagePattern":"Line length cannot be negative","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"document-readers/spring-ai-pdf-document-reader/src/main/java/org/springframework/ai/reader/pdf/layout/TextLine.java","lineNumber":38,"sourceCode":"\n/*\n * @author Soby Chacko\n * @author Tibor Tarnai\n */\n\nclass TextLine {\n\n\tprivate static final char SPACE_CHARACTER = ' ';\n\n\tprivate final int lineLength;\n\n\tprivate final char[] line;\n\n\tprivate int lastIndex;\n\n\tTextLine(int lineLength) {\n\t\tif (lineLength < 0) {\n\t\t\tthrow new IllegalArgumentException(\"Line length cannot be negative\");\n\t\t}\n\t\telse if (lineLength > 14_400) {\n\t\t\t// Cap to a reasonable limit to prevent attack via excessive char allocation\n\t\t\t// below.\n\t\t\t// 14_400 pdf units is the recommendation for the max dimension of a page by\n\t\t\t// ISO 32000\n\t\t\tthrow new IllegalArgumentException(\"Unreasonable lineLength of %d provided\".formatted(lineLength));\n\t\t}\n\t\tthis.lineLength = lineLength / ForkPDFLayoutTextStripper.OUTPUT_SPACE_CHARACTER_WIDTH_IN_PT;\n\t\tthis.line = new char[this.lineLength];\n\t\tArrays.fill(this.line, SPACE_CHARACTER);\n\t}\n\n\tpublic void writeCharacterAtIndex(final Character character) {\n\t\tcharacter.setIndex(this.computeIndexForCharacter(character));\n\t\tint index = character.getIndex();\n\t\tchar characterValue = character.getCharacterValue();\n\t\tif (this.indexIsInBounds(index) && this.line[index] == SPACE_CHARACTER) {","sourceCodeStart":20,"sourceCodeEnd":56,"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/TextLine.java#L20-L56","documentation":"TextLine's package constructor validates the computed line length before allocating its char buffer. A negative lineLength means upstream PDF layout math produced an invalid (negative) width/position delta, so an IllegalArgumentException is thrown instead of allocating a broken array.","triggerScenarios":"PDF text extraction where a computed line length from text positions (e.g. negative x-position deltas or negative scaling in text matrices) yields a value < 0 passed to the TextLine constructor during writeTextPositionList processing.","commonSituations":"Malformed PDFs with negative coordinates or bad transformation matrices; files generated by buggy producers; fuzzed/adversarial inputs to ForkPDFLayoutTextStripper.","solutions":["Sanitize the PDF (qpdf --clean / ghostscript rewrite) to fix invalid text positioning before extraction.","Disable layout-aware extraction (use plain text extraction) to bypass TextLine construction.","Catch IllegalArgumentException from DocumentReader.get() and skip the offending file.","Report/fix the upstream coordinate computation if it comes from your own PDF-generating code."],"exampleFix":"// before\nList<Document> docs = new PagePdfDocumentReader(badResource,\n    PagePdfDocumentReader.config().withLayoutEnabled()).get();\n\n// after\nList<Document> docs;\ntry {\n    docs = new PagePdfDocumentReader(badResource,\n        PagePdfDocumentReader.config().withLayoutEnabled()).get();\n} catch (IllegalArgumentException e) {\n    docs = List.of(); // or fall back to non-layout extraction\n}","handlingStrategy":"validation","validationCode":"try (PDDocument doc = Loader.loadPDF(file)) {\n    for (PDPage page : doc.getPages()) {\n        PDRectangle mb = page.getMediaBox();\n        if (mb.getWidth() < 0 || mb.getHeight() < 0)\n            throw new IllegalArgumentException(\"negative page dimension: \" + mb);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    List<Document> docs = new PagePdfDocumentReader(resource,\n        PagePdfDocumentReader.config().withLayoutEnabled()).get();\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"Line length cannot be negative\")) {\n        log.warn(\"Skipping PDF with invalid text geometry: {}\", resource);\n        docs = List.of();\n    } else throw e;\n}","preventionTips":["Validate media box dimensions (non-negative, within ISO 32000 limits) before extraction","Rewrite generator output so text matrices never produce negative position deltas","Use non-layout extraction for untrusted documents to bypass TextLine allocation entirely","Skip-and-log such documents in batch ingestion rather than failing the whole job"],"tags":["pdf","invalid-argument","layout","text-extraction"],"backgroundTag":"invalid-argument-value","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"}