junit-team/junit5 · critical · IllegalStateException

Task was deferred but should have been executed synchronousl

Error message

Task was deferred but should have been executed synchronously: ${testTask}

What it means

Thrown as IllegalStateException by ExclusiveTask.execSync() (line 239-245) when exec() returns false. exec() returns false only when the task's resource lock is incompatible with locks already held on the current thread, so the task is deferred instead of run. execSync() is called for SAME_THREAD tasks and single-task invokeAll where deferral is not allowed; hence 'should have been executed synchronously'.

Source

Thrown at junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorService.java:242

		 *
		 * @return {@code null} always
		 */
		@Override
		public final Void getRawResult() {
			return null;
		}

		/**
		 * Requires null completion value.
		 */
		@Override
		protected final void setRawResult(Void mustBeNull) {
		}

		void execSync() {
			boolean completed = exec();
			if (!completed) {
				throw new IllegalStateException(
					"Task was deferred but should have been executed synchronously: " + testTask);
			}
		}

		@SuppressWarnings("try")
		@Override
		public boolean exec() {
			// Check if this task is compatible with the current resource lock, if there is any.
			// If not, we put this task in the thread local as a deferred task
			// and let the worker thread fork it once it is done with the current task.
			ResourceLock resourceLock = testTask.getResourceLock();
			ThreadLock threadLock = threadLocks.get();
			if (!threadLock.areAllHeldLocksCompatibleWith(resourceLock)) {
				threadLock.addDeferredTask(this);
				taskEventListener.deferred(testTask);
				// Return false to indicate that this task is not done yet
				// this means that .join() will wait.
				return false;

View on GitHub (pinned to 956246301e)

Solutions

  1. Review @ResourceLock / @Isolated usage on the tests involved in the failure; likely a lock combination that cannot run on the same thread.
  2. Avoid SAME_THREAD execution mode for tests that require mutually exclusive resources with their parents; use CONCURRENT or move them to separate classes.
  3. If using ForkJoinPoolHierarchicalTestExecutorService (deprecated since 6.1), switch to the WORKER_THREAD_POOL executor which handles lock contention differently.
  4. Report a JUnit bug if the lock/resource configuration is legitimate; the message indicates an executor-internal invariant was violated.

Example fix

// before: conflicting locks with same-thread parent
@Execution(ExecutionMode.SAME_THREAD)
@ResourceLock(value = "shared", mode = Mode.READ_WRITE)
class MyNestedTest { ... } // triggers deferral in ForkJoinPool executor

// after: drop the rigid lock or execution mode
@Execution(ExecutionMode.CONCURRENT)
class MyNestedTest { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Ensure SAME_THREAD tests do not require resources incompatible with their parent's locks
// In test code: avoid combining @Execution(SAME_THREAD) with @ResourceLock(mode=READ_WRITE)
// on a resource the parent already locks exclusively.

Try / catch

try {
    testTask.execute();
} catch (IllegalStateException e) {
    if (e.getMessage().contains("deferred but should have been executed")) {
        // log and fall back to CONCURRENT execution or disable parallelism
    }
    throw e;
}

Prevention

When it happens

Trigger: A SAME_THREAD test task (or the lone task in an invokeAll) cannot acquire its ResourceLock because the current worker thread already holds an incompatible lock (e.g. a child needs GLOBAL_READ_WRITE while the parent holds a read-write lock). This is an internal invariant violation in the ForkJoinPool executor's lock bookkeeping.

Common situations: Engine/extension bugs mixing @ResourceLock annotations with SAME_THREAD execution mode in ways the executor cannot satisfy; @Isolated tests nested inside concurrently-executing parents in the ForkJoinPool executor; corner cases with custom Node.task implementations returning conflicting resource locks.

Related errors


AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04). Data as JSON: /data/errors/f352784df77264fd.json. Report an issue: GitHub.