opendataloader-project/opendataloader-pdf · error · InvalidPdfFileException
'{}' is not a valid PDF file (corrupted or truncated content
Error message
'{}' is not a valid PDF file (corrupted or truncated content). What it means
InvalidPdfFileException (a checked IOException subtype) thrown when the %PDF- magic number IS present (it passed validatePdfMagicNumber) but veraPDF's new PDDocument(pdfName) threw a plain IOException while parsing the body. This means the file looks like a PDF at a glance but its internal structure is broken — interrupted download, missing/incorrect xref table, or garbage after the header. The original veraPDF IOException is preserved as getCause(). InvalidPasswordException is deliberately NOT wrapped here; it is rethrown so encrypted-PDF handling in callers takes over.
Source
Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/processors/DocumentProcessor.java:636
*/
public static void preprocessing(String pdfName, Config config) throws IOException {
LOGGER.log(Level.INFO, () -> "File name: " + pdfName);
validateTempDirWritable();
validatePdfMagicNumber(pdfName);
updateStaticContainers(config);
PDDocument pdDocument;
try {
pdDocument = new PDDocument(pdfName);
} catch (InvalidPasswordException pw) {
// Encrypted PDFs are not a content-validity failure — let the
// password-handling branch in callers (e.g. CLIMain) take over.
throw pw;
} catch (IOException cause) {
// Magic number was present, so the user expected a real PDF, but
// veraPDF could not parse the document (truncated download, body
// corruption, missing xref). Surface a friendly message instead
// of letting the raw veraPDF IOException leak as a stack trace.
throw new InvalidPdfFileException(
"'" + displayName(pdfName) + "' is not a valid PDF file (corrupted or truncated content).",
cause);
}
StaticResources.setDocument(pdDocument);
GFSAPDFDocument document = new GFSAPDFDocument(pdDocument);
// org.verapdf.gf.model.impl.containers.StaticContainers.setFlavour(Collections.singletonList(PDFAFlavour.WCAG_2_2));
StaticResources.setFlavour(Collections.singletonList(Objects.equals(pdDocument.getVersion(), 2.0F) ?
PDFFlavour.WCAG_2_2_PDF_2_0_HUMAN : PDFFlavour.WCAG_2_2_HUMAN));
StaticStorages.setIsFilterInvisibleLayers(config.getFilterConfig().isFilterHiddenOCG());
StaticContainers.setDocument(document);
if (config.isUseStructTree()) {
document.parseStructureTreeRoot();
if (document.getTree() != null) {
StaticLayoutContainers.setIsUseStructTree(true);
} else {
StaticLayoutContainers.setIsUseStructTree(false);
LOGGER.log(Level.WARNING, "The document has no structure tree. The 'use-struct-tree' option will be ignored.");
}View on GitHub (pinned to a7789b8e77)
Solutions
- Inspect getCause() for the veraPDF parse error — a missing/bad xref, unexpected EOF, or stream decode failure points at the corruption type.
- Re-download or re-export the source PDF; verify byte-size and a checksum against the original.
- Validate integrity externally with a PDF repair tool (qpdf --check, mutool clean) before re-running.
- If this fires on many files from one producer, the producer is emitting non-conformant PDFs — report upstream rather than patching per file.
Example fix
// before: raw IOException leaks as an opaque stack trace
try { pdDocument = new PDDocument(pdfName); }
catch (IOException e) { /* caller sees veraPDF internals */ }
// after: the library already wraps it; callers catch the friendly type
catch (InvalidPdfFileException e) {
log.error("{}: {}", e.getMessage(), e.getCause().getMessage());
return Result.badInput(e.getMessage());
} Defensive patterns
Strategy: validation
Validate before calling
// Validate parseability with qpdf before invoking the heavy pipeline: // qpdf --check file.pdf (exit 0 => structurally ok) // Or in-process, rely on the library's own validatePdfMagicNumber equivalent + a try-parse.
Type guard
static boolean isInvalidPdfFile(IOException e) {
return e instanceof InvalidPdfFileException;
} Try / catch
try {
OpenDataLoaderPDF.processFile(pdfName, config);
} catch (InvalidPdfFileException e) {
// header was present but body unparseable
reportBadInput(e.getMessage()); // '... corrupted or truncated content.'
if (e.getCause() != null) log.debug("veraPDF cause", e.getCause());
} Prevention
- Run qpdf --check (or mutool clean) on ingested PDFs to catch structural corruption before processing.
- Verify file size/checksum after downloads to catch truncation.
- Catch InvalidPdfFileException specifically rather than generic IOException so you can distinguish bad input from transient I/O.
When it happens
Trigger: Calling DocumentProcessor.processFile / extractContents / preprocessing / OpenDataLoaderPDF.processFile, or AutoTagger.tag, on a file whose first 1024 bytes contain %PDF- but whose body new PDDocument() cannot parse (throws IOException that is not InvalidPasswordException).
Common situations: A download was interrupted leaving a truncated file with a valid header. A file was transferred in text mode corrupting binary bytes. A malformed PDF from a buggy producer has a broken xref. An attacker/test payload has a real header followed by non-PDF content.
Related errors
- '{}' is not a valid PDF file (missing %PDF- header).
- Cannot use the temporary directory '{}'. PDF processing need
- Backend processing failed and fallback is disabled
AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14).
Data as JSON: /api/errors/7c7bd4c96cfa1277.
Report an issue: GitHub.