junit-team/junit5 · error · ExtensionConfigurationException

@TempDir field [<field>] must not be declared as final.

Error message

@TempDir field [<field>] must not be declared as final.

What it means

Thrown by TempDirectory.assertNonFinalField() when a field annotated with @TempDir is declared final. The TempDirectory extension injects the temp dir by reflectively setting the field, which is impossible for final fields, so the engine reports it as an ExtensionConfigurationException during beforeAll/beforeEach field injection.

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 956246301e)

Solutions

  1. Remove the `final` modifier from the @TempDir field.
  2. If immutability is desired, use a non-final field or inject via a @TempDir parameter instead of a field.

Example fix

// before
@TempDir final Path tmp; // ExtensionConfigurationException
// after
@TempDir Path tmp;
Defensive patterns

Strategy: validation

Validate before calling

// Scan for final @TempDir fields at suite load
for (Field f : testClass.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: Declaring `private final Path dir;` or `final File tmp;` with @TempDir in a test class. The check runs in injectFields() for both static (@BeforeAll phase) and instance (@BeforeEach phase) fields.

Common situations: Using Lombok @val/@Value or a final modifier by habit; Kotlin val properties translated to final fields; migrating a field to final for immutability without realizing the extension needs to write it.

Related errors


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