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

  1. Inspect the chained cause: it states which configuration value was illegal.
  2. Check junit.execution.parallel.config.* properties in junit-platform.properties: ensure parallelism >= 1, maxPoolSize >= parallelism, corePoolSize >= 1, minimumRunnable >= 0.
  3. If using a custom org.junit.platform.engine.support.hierarchical.ParallelExecutionConfigurationSupplier, validate its returned ParallelExecutionConfiguration values.
  4. 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

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


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 significantly

View on GitHub (pinned to f070c699a0)