{"id":"ea1fbce8c5366049","repo":"junit-team/junit5","slug":"tempdir-field-field-must-not-be-declared-as-f","errorCode":null,"errorMessage":"@TempDir field [<field>] must not be declared as final.","messagePattern":"@TempDir field \\[<field>\\] must not be declared as final\\.","errorType":"exception","errorClass":"ExtensionConfigurationException","httpStatus":null,"severity":"error","filePath":"junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java","lineNumber":204,"sourceCode":"\n\tprivate Supplier<TempDirDeletionStrategy> determineDeletionStrategy(TempDir annotation) {\n\t\tvar strategyClass = annotation.deletionStrategy();\n\t\treturn strategyClass == TempDirDeletionStrategy.class //\n\t\t\t\t? this.configuration.getDefaultTempDirDeletionStrategySupplier() //\n\t\t\t\t: () -> ReflectionSupport.newInstance(strategyClass);\n\t}\n\n\tprivate TempDirFactory determineTempDirFactory(TempDir tempDir) {\n\t\tClass<? extends TempDirFactory> factory = tempDir.factory();\n\n\t\treturn factory == TempDirFactory.class //\n\t\t\t\t? this.configuration.getDefaultTempDirFactorySupplier().get()\n\t\t\t\t: ReflectionSupport.newInstance(factory);\n\t}\n\n\tprivate static void assertNonFinalField(Field field) {\n\t\tif (ModifierSupport.isFinal(field)) {\n\t\t\tthrow new ExtensionConfigurationException(\"@TempDir field [\" + field + \"] must not be declared as final.\");\n\t\t}\n\t}\n\n\tprivate static void assertSupportedType(String target, Class<?> type) {\n\t\tif (type != Path.class && type != File.class) {\n\t\t\tthrow new ExtensionConfigurationException(\"Can only resolve @TempDir \" + target + \" of type \"\n\t\t\t\t\t+ Path.class.getName() + \" or \" + File.class.getName() + \" but was: \" + type.getName());\n\t\t}\n\t}\n\n\tprivate Object getPathOrFile(Class<?> elementType, AnnotatedElementContext elementContext,\n\t\t\tExtensionContext extensionContext, TempDir tempDir) {\n\t\tTempDirFactory factory = determineTempDirFactory(tempDir);\n\t\tCleanup cleanup = new Cleanup(determineCleanupMode(tempDir), determineDeletionStrategy(tempDir));\n\t\treturn getPathOrFile(elementType, elementContext, factory, cleanup, extensionContext);\n\t}\n\n\tprivate static Object getPathOrFile(Class<?> elementType, AnnotatedElementContext elementContext,","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/junit-team/junit5/blob/956246301e03d757539f7b76dd5a91f298637563/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java#L186-L222","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n@TempDir final Path tmp; // ExtensionConfigurationException\n// after\n@TempDir Path tmp;","handlingStrategy":"validation","validationCode":"// Scan for final @TempDir fields at suite load\nfor (Field f : testClass.getDeclaredFields()) {\n    if (f.isAnnotationPresent(TempDir.class) && Modifier.isFinal(f.getModifiers())) {\n        throw new IllegalStateException(\"@TempDir field \" + f + \" must not be final\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["junit-jupiter","tempdir","extension","field-injection","final"],"analyzedSha":"956246301e03d757539f7b76dd5a91f298637563","analyzedAt":"2026-08-04T19:25:17.049Z","schemaVersion":2}