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
- Remove the `final` modifier from the @TempDir field.
- 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
- Never mark @TempDir fields final.
- Prefer @TempDir parameters over fields when immutability matters.
- Watch for Kotlin val (final) properties — use a parameter or var/mutable backing field.
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
- Can only resolve @TempDir <target> of type java.nio.file.Pat
- Failed to create default temp directory
- temp directory must be a directory
- temp directory with non-default file system cannot be inject
- Failed to publish path
AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04).
Data as JSON: /data/errors/ea1fbce8c5366049.json.
Report an issue: GitHub.