junit-team/junit5 · error · PreconditionViolationException
thread mode must not be INFERRED
Error message
thread mode must not be INFERRED
What it means
Thrown by TimeoutInvocationFactory.create() when the ThreadMode passed in is INFERRED. INFERRED is a sentinel meaning 'resolve from the junit.jupiter.execution.timeout.thread.mode.default configuration parameter', and TimeoutExtension.resolveTimeoutThreadMode() resolves it to SAME_THREAD or SEPARATE_THREAD before the factory is called. This throw is an internal precondition assertion: if INFERRED reaches the factory, the resolution logic upstream failed.
Source
Thrown at junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactory.java:43
/**
* @since 5.9
*/
class TimeoutInvocationFactory {
private final Store store;
TimeoutInvocationFactory(Store store) {
this.store = Preconditions.notNull(store, "store must not be null");
}
<T extends @Nullable Object> Invocation<T> create(ThreadMode threadMode,
TimeoutInvocationParameters<T> parameters) {
Preconditions.notNull(parameters, "timeout invocation parameters must not be null");
return switch (Preconditions.notNull(threadMode, "thread mode must not be null")) {
case SAME_THREAD -> new SameThreadTimeoutInvocation<>(parameters,
getThreadExecutorForSameThreadInvocation());
case SEPARATE_THREAD -> new SeparateThreadTimeoutInvocation<>(parameters);
case INFERRED -> throw new PreconditionViolationException("thread mode must not be INFERRED");
};
}
@SuppressWarnings("resource")
private ScheduledExecutorService getThreadExecutorForSameThreadInvocation() {
return store.computeIfAbsent(SingleThreadExecutorResource.class).get();
}
@SuppressWarnings({ "deprecation", "try" })
abstract static class ExecutorResource implements Store.CloseableResource, AutoCloseable {
private final ScheduledExecutorService executor;
ExecutorResource(ScheduledExecutorService executor) {
this.executor = executor;
}
ScheduledExecutorService get() {View on GitHub (pinned to 956246301e)
Solutions
- Resolve ThreadMode.INFERRED to SAME_THREAD or SEPARATE_THREAD before calling TimeoutInvocationFactory.create()
- If using the standard @Timeout annotation, ensure no custom extension intercepts and replaces the resolved thread mode with INFERRED
Defensive patterns
Strategy: validation
Validate before calling
// Before calling TimeoutInvocationFactory.create(), resolve INFERRED:
ThreadMode resolved = (threadMode == ThreadMode.INFERRED)
? ThreadMode.SAME_THREAD // or read junit.jupiter.execution.timeout.thread.mode.default
: threadMode;
assert resolved != ThreadMode.INFERRED; Prevention
- Always resolve ThreadMode.INFERRED to SAME_THREAD or SEPARATE_THREAD before passing it to TimeoutInvocationFactory
- This error is internal; if using standard @Timeout annotations it should never surface
When it happens
Trigger: Calling TimeoutInvocationFactory.create(ThreadMode.INFERRED, parameters) directly. In normal JUnit operation, TimeoutExtension always resolves INFERRED before calling the factory, so this is only reachable by custom extensions that bypass TimeoutExtension's resolution step or by internal bugs.
Common situations: Not encountered in normal test usage. Could appear if a custom Extension directly instantiates TimeoutInvocationFactory and passes an unresolved ThreadMode.INFERRED.
Related errors
- Could not map TimeUnit <unit> to ChronoUnit
- Timeout duration is not in the expected format (<number> [ns
- Scheduled executor could not be stopped in an orderly manner
- Unsupported argument count validation mode: <mode>
AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04).
Data as JSON: /data/errors/02f4f27f6787759d.json.
Report an issue: GitHub.