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

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.

Solutions

  1. Do not retain store references beyond the scope that owns them (the ExtensionContext/LauncherSession lifetime).
  2. 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).
  3. Ensure you are not double-closing or sharing a LauncherSession across independent runs.

Example fix

// before - extension caches the store in a field
private Store store;
void beforeAll(ExtensionContext ctx) { store = ctx.getStore(...); }
void afterAll() { store.put("done", true); } // ctx closed -> throws
// after - use the context each time
void afterAll(ExtensionContext ctx) { ctx.getStore(...).put("done", true); }
Defensive patterns

Strategy: validation

Validate before calling

// Track lifecycle: only touch the store while the owning context is open
if (sessionClosed) throw new IllegalStateException("store closed");
store.get(namespace, key, type);

Try / catch

try {
    store.put(namespace, key, value);
} catch (NamespacedHierarchicalStoreException e) {
    if (e.getMessage().contains("closed")) {
        // session/context gone: drop the write, do not retain the store
    }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11). Data as JSON: /api/errors/d80edc35df7df201. Report an issue: GitHub.

Appendix: 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();

		@Nullable

View on GitHub (pinned to f070c699a0)