junit-team/junit5 · error · PreconditionViolationException

temp directory must be a directory

Error message

temp directory must be a directory

What it means

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.

Source

Thrown at junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java:273

	static class CloseablePath implements Store.CloseableResource, AutoCloseable {

		private final @Nullable Path dir;
		private final TempDirFactory factory;
		private final Cleanup cleanup;
		private final AnnotatedElementContext elementContext;
		private final ExtensionContext extensionContext;

		private CloseablePath(TempDirFactory factory, Cleanup cleanup, Class<?> elementType,
				AnnotatedElementContext elementContext, ExtensionContext extensionContext) throws Exception {
			this.dir = factory.createTempDirectory(elementContext, extensionContext);
			this.factory = factory;
			this.cleanup = cleanup;
			this.elementContext = elementContext;
			this.extensionContext = extensionContext;

			if (this.dir == null || !Files.isDirectory(this.dir)) {
				close();
				throw new PreconditionViolationException("temp directory must be a directory");
			}

			if (elementType == File.class && !this.dir.getFileSystem().equals(FileSystems.getDefault())) {
				close();
				throw new PreconditionViolationException(
					"temp directory with non-default file system cannot be injected into " + File.class.getName()
							+ " target");
			}
		}

		Path get() {
			return requireNonNull(this.dir);
		}

		@Override
		public void close() throws IOException {
			try {
				if (this.dir != null) {

View on GitHub (pinned to 956246301e)

Solutions

  1. Fix the custom TempDirFactory to always return a non-null Path pointing at an existing directory (use Files.createTempDirectory).
  2. Never return null from createTempDirectory — create the directory first.
  3. If the path may be deleted concurrently, create it in a retry loop or under a lock.

Example fix

// before
class MyFactory implements TempDirFactory {
    public Path createTempDirectory(AnnotatedElementContext c, ExtensionContext ec) {
        return Path.of("/tmp/mydir"); // may not exist or may be a file
    }
}
// after
class MyFactory implements TempDirFactory {
    public Path createTempDirectory(AnnotatedElementContext c, ExtensionContext ec) throws Exception {
        return Files.createTempDirectory("mydir-");
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate a custom factory's output before wiring it
Path p = factory.createTempDirectory(elementContext, extensionContext);
if (p == null || !Files.isDirectory(p)) {
    throw new IllegalStateException("Factory must return an existing directory, got: " + p);
}

Type guard

static boolean factoryReturnsDirectory(TempDirFactory f, AnnotatedElementContext ec, ExtensionContext ctx) throws Exception {
    Path p = f.createTempDirectory(ec, ctx);
    return p != null && Files.isDirectory(p);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04). Data as JSON: /data/errors/5845f25dbb060efd.json. Report an issue: GitHub.