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

  1. Inspect getCause() for the veraPDF parse error — a missing/bad xref, unexpected EOF, or stream decode failure points at the corruption type.
  2. Re-download or re-export the source PDF; verify byte-size and a checksum against the original.
  3. Validate integrity externally with a PDF repair tool (qpdf --check, mutool clean) before re-running.
  4. 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

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


AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14). Data as JSON: /api/errors/7c7bd4c96cfa1277. Report an issue: GitHub.