junit-team/junit5 · error · NamespacedHierarchicalStoreException
A NamespacedHierarchicalStore cannot be modified or queried
Error message
A NamespacedHierarchicalStore cannot be modified or queried after it has been closed
What it means
Thrown by NamespacedHierarchicalStore.rejectIfClosed() (line 498-503) as a NamespacedHierarchicalStoreException whenever any mutating or querying method is called after close() has been invoked. The store's closed flag is set in the finally of close(); after that, get/put/remove/computeIfAbsent/getOrComputeIfAbsent all reject. Closing is irreversible (close() is idempotent but cannot reopen).
Source
Thrown at junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java:500
}
@SuppressWarnings("unchecked")
private <T, V> T castNonNullToRequiredType(Object key, V value, Class<T> requiredType) {
if (isAssignableTo(value, requiredType)) {
if (requiredType.isPrimitive()) {
return (T) requireNonNull(getWrapperType(requiredType)).cast(value);
}
return requiredType.cast(value);
}
// else
throw new NamespacedHierarchicalStoreException(
"Object stored under key [%s] is not of required type [%s], but was [%s]: %s".formatted(key,
requiredType.getName(), value.getClass().getName(), value));
}
private void rejectIfClosed() {
if (this.closed) {
throw new NamespacedHierarchicalStoreException(
"A NamespacedHierarchicalStore cannot be modified or queried after it has been closed");
}
}
private record CompositeKey<N>(N namespace, Object key) {
CompositeKey {
Preconditions.notNull(namespace, "namespace must not be null");
Preconditions.notNull(key, "key must not be null");
}
}
private interface StoredValue {
int order();
@NullableView on GitHub (pinned to 956246301e)
Solutions
- Ensure all work that touches the Store completes before the extension/test method returns (join threads, cancel scheduled tasks).
- Do not retain Store/ExtensionContext references beyond their documented scope.
- If you need cross-test state, use a dedicated namespace in an engine-level store or an external mechanism, not a per-test store.
- Check store.isClosed() defensively before access in long-lived/background code paths.
Example fix
// before
private ExtensionContext.Store cached;
@BeforeEach void cache(ExtensionContext ctx) { this.cached = ctx.getStore(...); }
@AfterAll void useStoreAfterTests() { cached.get("x"); } // store for a prior test already closed -> throws
// after
@AfterEach void useStoreInScope(ExtensionContext ctx) {
ExtensionContext.Store store = ctx.getStore(...); // fresh, in-scope
store.get("x");
} Defensive patterns
Strategy: validation
Validate before calling
if (store.isClosed()) {
throw new IllegalStateException("store already closed; do not access out of scope");
}
Object value = store.get(MY_NS, key); Type guard
static boolean isStoreUsable(ExtensionContext.Store store) {
// ExtensionContext.Store does not expose isClosed; treat any post-test access as unsafe.
// Only access within the callback that received the Store.
return true; // scope discipline is the real guard
} Try / catch
try {
return store.get(MY_NS, key);
} catch (NamespacedHierarchicalStoreException e) {
if (e.getMessage().contains("closed")) {
// background work outlived the test; reschedule or skip
log.debug("store closed; skipping stale access", e);
return null;
}
throw e;
} Prevention
- Never retain Store/ExtensionContext references beyond their callback scope.
- Join/cancel all background tasks before the test or extension method returns.
- Use engine-level namespaces for cross-test state, not per-test stores.
- Check isClosed() defensively before any access from long-lived code.
When it happens
Trigger: An extension or engine code path holds a reference to an ExtensionContext.Store (backed by NamespacedHierarchicalStore) and tries to read/write it after the corresponding test/extension scope has finished (e.g. a background thread outliving the test, or an after-callback reusing a captured store). Parent/child store closing does not close each other, but the store for a finished test is closed.
Common situations: Async work started in a test method that touches the Store after @AfterEach; extension @AfterEach callbacks touching a store whose engine-level parent closed; caching a Store in a static field and reusing across tests; thread pools not joined before test completion.
Related errors
- Object stored under key [%s] is not of required type [%s], b
- Launcher session has already been closed
- Failed to close extension context
- TestPlan must only be executed once
- Failed to close XML events file
AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04).
Data as JSON: /data/errors/d80edc35df7df201.json.
Report an issue: GitHub.