{"id":"5845f25dbb060efd","repo":"junit-team/junit5","slug":"temp-directory-must-be-a-directory","errorCode":null,"errorMessage":"temp directory must be a directory","messagePattern":"temp directory must be a directory","errorType":"exception","errorClass":"PreconditionViolationException","httpStatus":null,"severity":"error","filePath":"junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java","lineNumber":273,"sourceCode":"\tstatic class CloseablePath implements Store.CloseableResource, AutoCloseable {\n\n\t\tprivate final @Nullable Path dir;\n\t\tprivate final TempDirFactory factory;\n\t\tprivate final Cleanup cleanup;\n\t\tprivate final AnnotatedElementContext elementContext;\n\t\tprivate final ExtensionContext extensionContext;\n\n\t\tprivate CloseablePath(TempDirFactory factory, Cleanup cleanup, Class<?> elementType,\n\t\t\t\tAnnotatedElementContext elementContext, ExtensionContext extensionContext) throws Exception {\n\t\t\tthis.dir = factory.createTempDirectory(elementContext, extensionContext);\n\t\t\tthis.factory = factory;\n\t\t\tthis.cleanup = cleanup;\n\t\t\tthis.elementContext = elementContext;\n\t\t\tthis.extensionContext = extensionContext;\n\n\t\t\tif (this.dir == null || !Files.isDirectory(this.dir)) {\n\t\t\t\tclose();\n\t\t\t\tthrow new PreconditionViolationException(\"temp directory must be a directory\");\n\t\t\t}\n\n\t\t\tif (elementType == File.class && !this.dir.getFileSystem().equals(FileSystems.getDefault())) {\n\t\t\t\tclose();\n\t\t\t\tthrow new PreconditionViolationException(\n\t\t\t\t\t\"temp directory with non-default file system cannot be injected into \" + File.class.getName()\n\t\t\t\t\t\t\t+ \" target\");\n\t\t\t}\n\t\t}\n\n\t\tPath get() {\n\t\t\treturn requireNonNull(this.dir);\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws IOException {\n\t\t\ttry {\n\t\t\t\tif (this.dir != null) {","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/junit-team/junit5/blob/956246301e03d757539f7b76dd5a91f298637563/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java#L255-L291","documentation":"Thrown by CloseablePath's constructor when the TempDirFactory.createTempDirectory() returns a Path that is null or does not point to an existing directory (Files.isDirectory is false). The constructor closes the (already-created) factory and throws a PreconditionViolationException, ensuring no half-initialised CloseablePath leaks.","triggerScenarios":"A custom TempDirFactory returns null, returns a path to a regular file, or returns a path that was concurrently deleted before the constructor's isDirectory check. The default factory never triggers this because Files.createTempDirectory always returns a real directory.","commonSituations":"A custom factory that returns a hardcoded Path.of(\"/some/path\") which is a file or does not exist; a factory that returns null as a sentinel; a race where another process deletes the created directory between creation and validation.","solutions":["Fix the custom TempDirFactory to always return a non-null Path pointing at an existing directory (use Files.createTempDirectory).","Never return null from createTempDirectory — create the directory first.","If the path may be deleted concurrently, create it in a retry loop or under a lock."],"exampleFix":"// before\nclass MyFactory implements TempDirFactory {\n    public Path createTempDirectory(AnnotatedElementContext c, ExtensionContext ec) {\n        return Path.of(\"/tmp/mydir\"); // may not exist or may be a file\n    }\n}\n// after\nclass MyFactory implements TempDirFactory {\n    public Path createTempDirectory(AnnotatedElementContext c, ExtensionContext ec) throws Exception {\n        return Files.createTempDirectory(\"mydir-\");\n    }\n}","handlingStrategy":"validation","validationCode":"// Validate a custom factory's output before wiring it\nPath p = factory.createTempDirectory(elementContext, extensionContext);\nif (p == null || !Files.isDirectory(p)) {\n    throw new IllegalStateException(\"Factory must return an existing directory, got: \" + p);\n}","typeGuard":"static boolean factoryReturnsDirectory(TempDirFactory f, AnnotatedElementContext ec, ExtensionContext ctx) throws Exception {\n    Path p = f.createTempDirectory(ec, ctx);\n    return p != null && Files.isDirectory(p);\n}","tryCatchPattern":null,"preventionTips":["Custom TempDirFactory should use Files.createTempDirectory which always returns a real directory.","Never return null from createTempDirectory.","Unit-test the factory returns a directory that exists."],"tags":["junit-jupiter","tempdir","extension","precondition","filesystem"],"analyzedSha":"956246301e03d757539f7b76dd5a91f298637563","analyzedAt":"2026-08-04T19:25:17.049Z","schemaVersion":2}