opendataloader-project/opendataloader-pdf · critical · EnvironmentNotUsableException
Cannot use the temporary directory '{tempDir}'. PDF processi
Error message
Cannot use the temporary directory '{tempDir}'. PDF processing needs it to read font metrics and decode content streams. Point java.io.tmpdir or the TMPDIR environment variable at a writable directory, or grant write access to this one. What it means
The JVM's temporary directory (java.io.tmpdir / TMPDIR) must be both writable AND deletable by the process. PDF processing spills unbounded streams to temp files (Standard-14 font metrics, embedded font programs, CMaps, decoded content streams) because a PDF stream has no size bound while memory does, so buffering cannot avoid it. validateTempDirWritable() probes by creating then deleting a temp file under Path.of(java.io.tmpdir); any IOException or RuntimeException (including InvalidPathException from a malformed -Djava.io.tmpdir) triggers TempDirectoryNotWritableException. In the CLI it is caught at CLIMain.java:264 and rethrown as EnvironmentNotUsableException (a RuntimeException) to abort the whole batch, since every remaining file would fail identically.
Source
Thrown at java/opendataloader-pdf-cli/src/main/java/org/opendataloader/pdf/cli/CLIMain.java:267
System.out.println("Error: " + invalid.getMessage());
return false;
}
LOGGER.log(Level.WARNING, invalid.getMessage() + " Skipping.");
return true;
} catch (InvalidPasswordException exception) {
String password = config.getPassword();
String message = (password == null || password.isEmpty())
? "Error: '" + file.getName() + "' is password-protected. Use --password option."
: "Error: Incorrect password for '" + file.getName() + "'.";
System.out.println(message);
return false;
} catch (EncryptedTaggedPdfNotSupportedException exception) {
System.out.println("Error: " + exception.getMessage());
return false;
} catch (TempDirectoryNotWritableException exception) {
// Environment failure, not a problem with this file: every remaining
// file would fail the same way. Abort instead of repeating it per file.
throw new EnvironmentNotUsableException(exception);
} catch (Exception exception) {
LOGGER.log(Level.SEVERE, "Exception during processing file " + file.getAbsolutePath() + ": " +
exception.getMessage());
return false;
} finally {
StaticContainers.closeImagesUtils();
}
}
private static boolean isPdfFile(File file) {
if (!file.isFile()) {
return false;
}
String name = file.getName();
return name.toLowerCase(Locale.ROOT).endsWith(".pdf");
}
}
View on GitHub (pinned to a7789b8e77)
Solutions
- Point the temp dir at a writable location: add -Djava.io.tmpdir=/tmp to the JVM args or export TMPDIR=/tmp.
- In containers, mount a writable volume at /tmp (emptyDir/tmpfs in k8s, --tmpfs /tmp in Docker) or drop --read-only / readOnlyRootFilesystem.
- Grant write+delete permission (chmod) or ownership (chown) on the existing temp directory to the JVM user.
- Ensure the temp path exists and is not a malformed/relative value passed to -Djava.io.tmpdir.
Example fix
// before: docker run --read-only myimg pdf --in f.pdf // after: docker run --read-only --tmpfs /tmp myimg pdf --in f.pdf // or JVM: java -Djava.io.tmpdir=/tmp -jar pdf.jar ...
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: confirm java.io.tmpdir is create+delete writable before processing.
String tempDir = System.getProperty("java.io.tmpdir");
try {
Path probe = Files.createTempFile(Path.of(tempDir), "opendataloader-", ".probe");
Files.delete(probe);
} catch (IOException | RuntimeException e) {
System.err.println("Temp dir not usable: " + tempDir + " (" + e + ")");
System.setProperty("java.io.tmpdir", "/tmp"); // or fail fast
} Try / catch
// Public API path: TempDirectoryNotWritableException is a CHECKED IOException.
try {
OpenDataLoaderPDF.processFile(path, config);
} catch (TempDirectoryNotWritableException e) {
// Environment failure: stop the whole batch, do not retry per file.
throw new IllegalStateException("Temp dir unusable; aborting batch", e);
}
// CLI path: it surfaces as the unchecked EnvironmentNotUsableException.
try { cliMain(args); }
catch (RuntimeException e) { /* EnvironmentNotUsableException is private; treat as fatal env error */ } Prevention
- In containers, always mount a writable volume (tmpfs/emptyDir) at the temp dir and avoid --read-only without it.
- Set TMPDIR explicitly in CI and Docker so it points at a known-writable path.
- Treat this as a batch-level abort, not a per-file retry: every file fails the same way.
When it happens
Trigger: Called from the start of OpenDataLoaderPDF.processFile, DocumentProcessor.processFile/processFileWithResult/extractContents/preprocessing, or AutoTagger.tag when System.getProperty("java.io.tmpdir") resolves to a read-only, non-deletable, or malformed path. The probe fails on create (Files.createTempFile) or on delete (Files.delete) when a sticky bit is owned by another user.
Common situations: Docker/k8s with --read-only or readOnlyRootFilesystem and no writable /tmp; restricted CI runners (GitHub Actions, sandboxed executors); SELinux/AppArmor confinement; TMPDIR exported to a deleted or unmounted directory; -Djava.io.tmpdir pointing at a path the JVM user cannot own.
Related errors
- Cannot use the temporary directory '{}'. PDF processing need
- Unsupported table method '%s'. Supported values: %s
- Unsupported reading order '%s'. Supported values: %s
- Unsupported image output mode '%s'. Supported values: %s
- Unsupported image format '%s'. Supported values: %s
AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14).
Data as JSON: /api/errors/33e2aad131fdcbc6.
Report an issue: GitHub.