junit-team/junit5 · critical · JUnitException
Failed to create ForkJoinPool
Error message
Failed to create ForkJoinPool
What it means
Wraps any exception thrown while constructing the java.util.concurrent.ForkJoinPool used by ForkJoinPoolHierarchicalTestExecutorService. The pool is built from a ParallelExecutionConfiguration (parallelism, core/max pool size, minimum runnable, saturate predicate, keep-alive). If the JDK rejects those parameters (e.g. illegal parallelism, negative sizes, or an UnsupportedOperation from the configuration source) the original cause is chained.
Solutions
- Inspect the chained cause: it states which configuration value was illegal.
- Check junit.execution.parallel.config.* properties in junit-platform.properties: ensure parallelism >= 1, maxPoolSize >= parallelism, corePoolSize >= 1, minimumRunnable >= 0.
- If using a custom org.junit.platform.engine.support.hierarchical.ParallelExecutionConfigurationSupplier, validate its returned ParallelExecutionConfiguration values.
- Temporarily disable parallel execution (junit.execution.parallel.enabled=false) to confirm the config is the culprit.
Example fix
// before (junit-platform.properties) junit.execution.parallel.enabled=true junit.execution.parallel.config.fixed.parallelism=0 junit.execution.parallel.config.fixed.max-pool-size=1 // after junit.execution.parallel.enabled=true junit.execution.parallel.config.fixed.parallelism=4 junit.execution.parallel.config.fixed.max-pool-size=4
Defensive patterns
Strategy: validation
Validate before calling
// Validate parallel config before launching
int p = readInt("junit.execution.parallel.config.fixed.parallelism", 1);
int max = readInt("junit.execution.parallel.config.fixed.max-pool-size", p);
int core = readInt("junit.execution.parallel.config.fixed.core-pool-size", p);
if (p < 1 || max < p || core < 1) throw new IllegalArgumentException("bad parallel config"); Try / catch
try {
launcher.execute(request);
} catch (JUnitException e) {
Throwable c = e.getCause();
if (c instanceof JUnitException jc && jc.getMessage().contains("Failed to create ForkJoinPool")) {
// fix junit.execution.parallel.config.* and retry
}
throw e;
} Prevention
- Keep maxPoolSize >= parallelism >= 1 and corePoolSize >= 1 in junit-platform.properties.
- Unit-test custom ParallelExecutionConfigurationSupplier outputs for valid ranges.
- Disable parallel execution when diagnosing to isolate config issues.
When it happens
Trigger: The engine creates its executor service and calls createForkJoinPool(configuration); ForkJoinPool's constructor throws IllegalArgumentException or IllegalStateException because configuration.getParallelism()/getCorePoolSize()/getMaxPoolSize()/getMinimumRunnable() returned inconsistent values, or parallelism <= 0, or maxPoolSize < parallelism, or a saturate predicate implementation fails.
Common situations: Misconfigured junit.execution.parallel.config.* properties (e.g. parallelism=0, maxPoolSize smaller than parallelism, minimumRunnable negative); a custom ParallelExecutionConfigurationSupplier returning bad values; running on a JVM/JDK where ForkJoinPool constructor semantics changed; resource starvation where a pool cannot be allocated.
Related errors
- Cannot override the standard style 'NONE'
- Configuration error: .
- Configuration error: You must configure at least one set of…
- Constructor injection is not supported for…
- Could not open color palette properties file
AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11).
Data as JSON: /api/errors/d214662d979ae93c.
Report an issue: GitHub.
Appendix: source
Thrown at junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorService.java:111
this(configuration, TaskEventListener.NOOP);
}
ForkJoinPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration,
TaskEventListener taskEventListener) {
forkJoinPool = createForkJoinPool(configuration);
this.taskEventListener = taskEventListener;
parallelism = forkJoinPool.getParallelism();
LoggerFactory.getLogger(getClass()).config(() -> "Using ForkJoinPool with parallelism of " + parallelism);
}
private ForkJoinPool createForkJoinPool(ParallelExecutionConfiguration configuration) {
try {
return new ForkJoinPool(configuration.getParallelism(), new WorkerThreadFactory(), null, false,
configuration.getCorePoolSize(), configuration.getMaxPoolSize(), configuration.getMinimumRunnable(),
configuration.getSaturatePredicate(), configuration.getKeepAliveSeconds(), TimeUnit.SECONDS);
}
catch (Exception cause) {
throw new JUnitException("Failed to create ForkJoinPool", cause);
}
}
@Override
public Future<@Nullable Void> submit(TestTask testTask) {
ExclusiveTask exclusiveTask = new ExclusiveTask(testTask);
if (!isAlreadyRunningInForkJoinPool()) {
// ensure we're running inside the ForkJoinPool so we
// can use ForkJoinTask API in invokeAll etc.
return forkJoinPool.submit(exclusiveTask);
}
// Limit the amount of queued work so we don't consume dynamic tests too eagerly
// by forking only if the current worker thread's queue length is below the
// desired parallelism. This optimistically assumes that the already queued tasks
// can be stolen by other workers and the new task requires about the same
// execution time as the already queued tasks. If the other workers are busy,
// the parallelism is already at its desired level. If all already queued tasks
// can be stolen by otherwise idle workers and the new task takes significantlyView on GitHub (pinned to f070c699a0)