{"record":{"id":"d80edc35df7df201","repo":"junit-team/junit5","slug":"a-namespacedhierarchicalstore-cannot-be-modified-o","errorCode":null,"errorMessage":"A NamespacedHierarchicalStore cannot be modified or queried after it has been closed","messagePattern":"A NamespacedHierarchicalStore cannot be modified or queried after it has been closed","errorType":"exception","errorClass":"NamespacedHierarchicalStoreException","httpStatus":null,"severity":"error","filePath":"junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java","lineNumber":500,"sourceCode":"\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T, V> T castNonNullToRequiredType(Object key, V value, Class<T> requiredType) {\n\t\tif (isAssignableTo(value, requiredType)) {\n\t\t\tif (requiredType.isPrimitive()) {\n\t\t\t\treturn (T) requireNonNull(getWrapperType(requiredType)).cast(value);\n\t\t\t}\n\t\t\treturn requiredType.cast(value);\n\t\t}\n\t\t// else\n\t\tthrow new NamespacedHierarchicalStoreException(\n\t\t\t\"Object stored under key [%s] is not of required type [%s], but was [%s]: %s\".formatted(key,\n\t\t\t\trequiredType.getName(), value.getClass().getName(), value));\n\t}\n\n\tprivate void rejectIfClosed() {\n\t\tif (this.closed) {\n\t\t\tthrow new NamespacedHierarchicalStoreException(\n\t\t\t\t\"A NamespacedHierarchicalStore cannot be modified or queried after it has been closed\");\n\t\t}\n\t}\n\n\tprivate record CompositeKey<N>(N namespace, Object key) {\n\n\t\tCompositeKey {\n\t\t\tPreconditions.notNull(namespace, \"namespace must not be null\");\n\t\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\t}\n\n\t}\n\n\tprivate interface StoredValue {\n\n\t\tint order();\n\n\t\t@Nullable","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java#L482-L518","documentation":"NamespacedHierarchicalStore rejects every get/put/remove operation after close() has been called via rejectIfClosed(). The store backs LauncherSession/ExtensionContext scoped data; closing the owning session/context invalidates it. Any later access is a use-after-close bug.","triggerScenarios":"Calling any mutating or querying method on a NamespacedHierarchicalStore after store.close() ran. Happens when an ExtensionContext or LauncherSession is closed but code retains a reference to its store and reads/writes it later (e.g. an extension that caches the store in a static or long-lived field).","commonSituations":"An extension stored the ExtensionContext.getStore() reference and uses it after the test/class finished; a LauncherSession scoped bean accessed after LauncherSession.close(); a listener that holds store state past the session; reuse of a closed EngineContext in a new test run.","solutions":["Do not retain store references beyond the scope that owns them (the ExtensionContext/LauncherSession lifetime).","If you must cache, copy values out of the store before the context closes (via a BeforeAllCallback/AfterAllCallback that runs while the store is open).","Ensure you are not double-closing or sharing a LauncherSession across independent runs."],"exampleFix":"// before - extension caches the store in a field\nprivate Store store;\nvoid beforeAll(ExtensionContext ctx) { store = ctx.getStore(...); }\nvoid afterAll() { store.put(\"done\", true); } // ctx closed -> throws\n// after - use the context each time\nvoid afterAll(ExtensionContext ctx) { ctx.getStore(...).put(\"done\", true); }","handlingStrategy":"validation","validationCode":"// Track lifecycle: only touch the store while the owning context is open\nif (sessionClosed) throw new IllegalStateException(\"store closed\");\nstore.get(namespace, key, type);","typeGuard":null,"tryCatchPattern":"try {\n    store.put(namespace, key, value);\n} catch (NamespacedHierarchicalStoreException e) {\n    if (e.getMessage().contains(\"closed\")) {\n        // session/context gone: drop the write, do not retain the store\n    }\n}","preventionTips":["Never store NamespacedHierarchicalStore references in static/long-lived fields.","Access the store only through the live ExtensionContext / LauncherSession.","Copy data out before the owning context closes."],"tags":["store","use-after-close","lifecycle","session"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}