spring-projects/spring-ai · error · IllegalStateException

Unreasonable number of lines (%d) computed from content of p

Error message

Unreasonable number of lines (%d) computed from content of pdf

What it means

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.

Source

Thrown at document-readers/spring-ai-pdf-document-reader/src/main/java/org/springframework/ai/reader/pdf/layout/ForkPDFLayoutTextStripper.java:140

		else {
			this.addNewLine(); // white line
		}
	}

	private void iterateThroughTextList(Iterator<TextPosition> textIterator) {
		List<TextPosition> textPositionList = new ArrayList<>();

		while (textIterator.hasNext()) {
			TextPosition textPosition = (TextPosition) textIterator.next();
			int numberOfNewLines = this.getNumberOfNewLinesFromPreviousTextPosition(textPosition);
			if (numberOfNewLines == 0) {
				textPositionList.add(textPosition);
			}
			else {
				this.writeTextPositionList(textPositionList);
				if (numberOfNewLines > 10_000) {
					// Throw rather than allocate crazy number of line objects
					throw new IllegalStateException("Unreasonable number of lines (%d) computed from content of pdf"
						.formatted(numberOfNewLines));
				}
				this.createNewEmptyNewLines(numberOfNewLines);
				textPositionList.add(textPosition);
			}
			this.setPreviousTextPosition(textPosition);
		}
		if (!textPositionList.isEmpty()) {
			this.writeTextPositionList(textPositionList);
		}
	}

	private void writeTextPositionList(final List<TextPosition> textPositionList) {
		this.writeLine(textPositionList);
		textPositionList.clear();
	}

	private void createNewEmptyNewLines(int numberOfNewLines) {

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Validate/repair the PDF: open it in a viewer or run it through a normalizer (qpdf/gs) to fix extreme text positioning before extraction.
  2. Extract text without layout mode (plain PDFTextStripper or PagePdfDocumentReader without layout) if exact layout is not required.
  3. Catch IllegalStateException and skip/quarantine the document in batch processing.
  4. If 10,000 lines is too low for legitimate documents, fork/raise the threshold in ForkPDFLayoutTextStripper.

Example fix

// before
DocumentReader reader = new PagePdfDocumentReader(resource, PagePdfDocumentReader.config().withLayoutEnabled());
List<Document> docs = reader.get(); // throws on huge line gaps

// after
List<Document> docs;
try {
    docs = new PagePdfDocumentReader(resource, PagePdfDocumentReader.config().withLayoutEnabled()).get();
} catch (IllegalStateException e) {
    docs = new PagePdfDocumentReader(resource).get(); // non-layout extraction
}
Defensive patterns

Strategy: validation

Validate before calling

try (PDDocument doc = Loader.loadPDF(file)) {
    for (PDPage page : doc.getPages()) {
        float h = page.getMediaBox().getHeight();
        if (h > 14_400 || h <= 0)
            throw new IllegalArgumentException("page height out of range: " + h);
    }
}

Try / catch

try {
    List<Document> docs = new PagePdfDocumentReader(resource,
        PagePdfDocumentReader.config().withLayoutEnabled()).get();
} catch (IllegalStateException e) {
    if (e.getMessage().startsWith("Unreasonable number of lines")) {
        docs = new PagePdfDocumentReader(resource).get(); // skip layout mode
    } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/71516ca152b09ba5. Report an issue: GitHub.