junit-team/junit5 · error · ExtensionConfigurationException

@TempDir field [%s] must not be declared as final.

Error message

@TempDir field [%s] must not be declared as final.

What it means

Thrown as ExtensionConfigurationException by assertNonFinalField when a field annotated with @TempDir is declared final. JUnit needs to assign the temp directory into the field after construction, so a final field would make injection impossible and is rejected during field setup.

Source

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

	private Supplier<TempDirDeletionStrategy> determineDeletionStrategy(TempDir annotation) {
		var strategyClass = annotation.deletionStrategy();
		return strategyClass == TempDirDeletionStrategy.class //
				? this.configuration.getDefaultTempDirDeletionStrategySupplier() //
				: () -> ReflectionSupport.newInstance(strategyClass);
	}

	private TempDirFactory determineTempDirFactory(TempDir tempDir) {
		Class<? extends TempDirFactory> factory = tempDir.factory();

		return factory == TempDirFactory.class //
				? this.configuration.getDefaultTempDirFactorySupplier().get()
				: ReflectionSupport.newInstance(factory);
	}

	private static void assertNonFinalField(Field field) {
		if (ModifierSupport.isFinal(field)) {
			throw new ExtensionConfigurationException("@TempDir field [" + field + "] must not be declared as final.");
		}
	}

	private static void assertSupportedType(String target, Class<?> type) {
		if (type != Path.class && type != File.class) {
			throw new ExtensionConfigurationException("Can only resolve @TempDir " + target + " of type "
					+ Path.class.getName() + " or " + File.class.getName() + " but was: " + type.getName());
		}
	}

	private Object getPathOrFile(Class<?> elementType, AnnotatedElementContext elementContext,
			ExtensionContext extensionContext, TempDir tempDir) {
		TempDirFactory factory = determineTempDirFactory(tempDir);
		Cleanup cleanup = new Cleanup(determineCleanupMode(tempDir), determineDeletionStrategy(tempDir));
		return getPathOrFile(elementType, elementContext, factory, cleanup, extensionContext);
	}

	private static Object getPathOrFile(Class<?> elementType, AnnotatedElementContext elementContext,

View on GitHub (pinned to f070c699a0)

Solutions

  1. Remove the final modifier from the @TempDir field (Java) or change Kotlin val to var.
  2. For Kotlin, use `lateinit var` or `var` with a nullable type.
  3. Prefer the @TempDir parameter form on @BeforeEach/lifecycle methods if you need immutability elsewhere.

Example fix

// before
@TempDir
private final Path tmp; // final -> rejected

// after
@TempDir
private Path tmp;

// Kotlin
// before:  val tmp: Path by ...  // val = final
// after:   @TempDir lateinit var tmp: Path  // (or nullable var)
Defensive patterns

Strategy: validation

Validate before calling

for (Field f : MyTest.class.getDeclaredFields()) {
    if (f.isAnnotationPresent(TempDir.class) && Modifier.isFinal(f.getModifiers())) {
        throw new IllegalStateException("@TempDir field " + f + " must not be final");
    }
}

Prevention

When it happens

Trigger: A field declared as `private final Path tmp;` or `static final File dir;` annotated with @TempDir - assertNonFinalField(field) is invoked during field/parameter resolution in TempDirectory and ModifierSupport.isFinal(field) is true.

Common situations: Kotlin val (which compiles to final) annotated with @TempDir; Java field declared final by habit; record components or migrated code that made the field final; Lombok @Value or @Final used on the field.

Related errors


AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11). Data as JSON: /api/errors/fb38cb6d8fed0a69. Report an issue: GitHub.