opendataloader-project/opendataloader-pdf · error · TempDirectoryNotWritableException
Cannot use the temporary directory '{}'. PDF processing need
Error message
Cannot use the temporary directory '{}'. 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
TempDirectoryNotWritableException (IOException subtype) thrown by validateTempDirWritable, which runs per-file before processing. It probes the JVM temp dir by creating AND deleting a file (Files.createTempFile + Files.delete), because veraPDF spills Standard-14 font metrics, embedded font programs, CMaps, and decoded content streams to disk and then deletes them — a dir that allows creation but blocks deletion would leak one file per stream. The check prevents a silent failure mode where veraPDF logs the I/O error at FINE, swallows it, and processing 'succeeds' (exit 0) while dropping most text or throwing an unrelated NPE deep inside.
Source
Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/processors/DocumentProcessor.java:742
* cannot be written to
*/
private static void validateTempDirWritable() throws TempDirectoryNotWritableException {
String tempDir = System.getProperty("java.io.tmpdir");
try {
// Resolve java.io.tmpdir explicitly rather than calling the no-arg
// Files.createTempFile: that one resolves the directory once when the
// JVM starts, so it would ignore a -Djava.io.tmpdir override applied
// later and probe a different directory than veraPDF ends up using.
// RuntimeException covers InvalidPathException from a malformed override.
Path probe = Files.createTempFile(Path.of(tempDir), "opendataloader-", ".probe");
// Deleting is part of what is being verified, not just cleanup: veraPDF
// removes its spill files, so a directory that allows creation but
// refuses deletion (a sticky bit the process does not own, say) would
// accumulate one file per stream. Failing here also keeps the probe
// itself from leaking.
Files.delete(probe);
} catch (IOException | RuntimeException e) {
throw new TempDirectoryNotWritableException(
"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.", e);
}
}
/**
* Path.getFileName() returns null for filesystem roots (e.g. {@code C:\}).
* Fall back to the original input string in that case so the user-facing
* error message is never empty.
*/
private static String displayName(String pdfName) {
Path fileName = Path.of(pdfName).getFileName();
return fileName != null ? fileName.toString() : pdfName;
}
private static int indexOfBytes(byte[] haystack, byte[] needle) {View on GitHub (pinned to a7789b8e77)
Solutions
- Point java.io.tmpdir (or TMPDIR) at a known-writable directory: run with -Djava.io.tmpdir=/var/tmp/odl or export TMPDIR=/var/tmp/odl after creating it.
- Grant write+delete permission on the existing temp dir to the running user (chmod/chown), or remove the sticky-bit ownership mismatch.
- Freeze disk space: check df -h on the temp dir's mount and clear space if full.
- In containers, mount an emptyDir/writable volume at /tmp or set TMPDIR to a writable emptyDir.
Example fix
# before: container runs with read-only /tmp $ odl-pdf doc.pdf -> TempDirectoryNotWritableException: Cannot use the temporary directory '/tmp'... # after: override to a writable dir $ mkdir -p /var/tmp/odl && TMPDIR=/var/tmp/odl odl-pdf doc.pdf
Defensive patterns
Strategy: validation
Validate before calling
// Probe the temp dir yourself before processing, mirroring the library's check:
Path tmp = Path.of(System.getProperty("java.io.tmpdir"));
Path probe = Files.createTempFile(tmp, "pre-", ".probe");
Files.delete(probe); // must also succeed
Type guard
static boolean isTempDirProblem(IOException e) {
return e instanceof TempDirectoryNotWritableException;
} Try / catch
try {
DocumentProcessor.extractContents(pdfName, config);
} catch (TempDirectoryNotWritableException e) {
// Environment issue, not a bad file — fix the dir, not the input.
String tmp = System.getProperty("java.io.tmpdir");
log.error("Temp dir {} not writable; set -Djava.io.tmpdir or TMPDIR", tmp, e);
throw new RuntimeException("Fix java.io.tmpdir then retry", e);
} Prevention
- Set -Djava.io.tmpdir (or TMPDIR) to a known-writable dir at JVM startup in containers.
- Mount a writable volume at /tmp in container images.
- Do NOT cache a successful probe result — java.io.tmpdir is mutable and the dir can be remounted read-only mid-process (the library re-probes per file on purpose).
When it happens
Trigger: validateTempDirWritable is called at the start of updateStaticContainers/extractContents: Files.createTempFile(Path.of(tempDir), ...) or the subsequent Files.delete(probe) throws IOException (permission denied, no space, read-only mount) or RuntimeException (InvalidPathException from a malformed -Djava.io.tmpdir value).
Common situations: Container/image with a read-only or nodev /tmp. A malformed -Djava.io.tmpdir=/bad:|path override. TMPDIR env var pointing at a deleted/unwritable dir. Disk full. A sticky-bit /tmp the process does not own (allows create, blocks delete). Read-only root filesystem in a hardened pod.
Related errors
- Cannot use the temporary directory '{tempDir}'. PDF processi
- '{}' is not a valid PDF file (corrupted or truncated content
- Backend processing failed and fallback is disabled
- {f}: {e}
AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14).
Data as JSON: /api/errors/8c7d788be898ca7e.
Report an issue: GitHub.