{"record":{"id":"33e2aad131fdcbc6","repo":"opendataloader-project/opendataloader-pdf","slug":"cannot-use-the-temporary-directory-tempdir-pd","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"exception","errorClass":"EnvironmentNotUsableException","httpStatus":null,"severity":"critical","filePath":"java/opendataloader-pdf-cli/src/main/java/org/opendataloader/pdf/cli/CLIMain.java","lineNumber":267,"sourceCode":"                System.out.println(\"Error: \" + invalid.getMessage());\n                return false;\n            }\n            LOGGER.log(Level.WARNING, invalid.getMessage() + \" Skipping.\");\n            return true;\n        } catch (InvalidPasswordException exception) {\n            String password = config.getPassword();\n            String message = (password == null || password.isEmpty())\n                ? \"Error: '\" + file.getName() + \"' is password-protected. Use --password option.\"\n                : \"Error: Incorrect password for '\" + file.getName() + \"'.\";\n            System.out.println(message);\n            return false;\n        } catch (EncryptedTaggedPdfNotSupportedException exception) {\n            System.out.println(\"Error: \" + exception.getMessage());\n            return false;\n        } catch (TempDirectoryNotWritableException exception) {\n            // Environment failure, not a problem with this file: every remaining\n            // file would fail the same way. Abort instead of repeating it per file.\n            throw new EnvironmentNotUsableException(exception);\n        } catch (Exception exception) {\n            LOGGER.log(Level.SEVERE, \"Exception during processing file \" + file.getAbsolutePath() + \": \" +\n                exception.getMessage());\n            return false;\n        } finally {\n            StaticContainers.closeImagesUtils();\n        }\n    }\n\n    private static boolean isPdfFile(File file) {\n        if (!file.isFile()) {\n            return false;\n        }\n        String name = file.getName();\n        return name.toLowerCase(Locale.ROOT).endsWith(\".pdf\");\n    }\n}\n","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/opendataloader-project/opendataloader-pdf/blob/a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8/java/opendataloader-pdf-cli/src/main/java/org/opendataloader/pdf/cli/CLIMain.java#L249-L285","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: docker run --read-only myimg pdf --in f.pdf\n// after:  docker run --read-only --tmpfs /tmp myimg pdf --in f.pdf\n// or JVM: java -Djava.io.tmpdir=/tmp -jar pdf.jar ...","handlingStrategy":"validation","validationCode":"// Pre-flight: confirm java.io.tmpdir is create+delete writable before processing.\nString tempDir = System.getProperty(\"java.io.tmpdir\");\ntry {\n    Path probe = Files.createTempFile(Path.of(tempDir), \"opendataloader-\", \".probe\");\n    Files.delete(probe);\n} catch (IOException | RuntimeException e) {\n    System.err.println(\"Temp dir not usable: \" + tempDir + \" (\" + e + \")\");\n    System.setProperty(\"java.io.tmpdir\", \"/tmp\"); // or fail fast\n}","typeGuard":null,"tryCatchPattern":"// Public API path: TempDirectoryNotWritableException is a CHECKED IOException.\ntry {\n    OpenDataLoaderPDF.processFile(path, config);\n} catch (TempDirectoryNotWritableException e) {\n    // Environment failure: stop the whole batch, do not retry per file.\n    throw new IllegalStateException(\"Temp dir unusable; aborting batch\", e);\n}\n// CLI path: it surfaces as the unchecked EnvironmentNotUsableException.\ntry { cliMain(args); }\ncatch (RuntimeException e) { /* EnvironmentNotUsableException is private; treat as fatal env error */ }","preventionTips":["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."],"tags":["environment","temp-dir","io","permissions","container","configuration"],"backgroundTag":null,"analyzedSha":"a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8","analyzedAt":"2026-08-14T05:22:03.953Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}