junit-team/junit5 · error · JUnitException

Chain of InvocationInterceptors called invocation multiple t

Error message

Chain of InvocationInterceptors called invocation multiple times instead of just once: %s

What it means

Internal assertion in ValidatingInvocation.markInvokedOrSkipped: the AtomicBoolean invokedOrSkipped was already true when proceed()/skip() ran, so an interceptor invoked (or skipped) the chain more than once. Each intercepted call must proceed exactly once; double-proceed corrupts execution and is rejected with the interceptor list appended.

Source

Thrown at junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java:149

			delegate.skip();
		}

		private void markInvokedOrSkipped() {
			if (!invokedOrSkipped.compareAndSet(false, true)) {
				fail("Chain of InvocationInterceptors called invocation multiple times instead of just once");
			}
		}

		void verifyInvokedAtLeastOnce() {
			if (!invokedOrSkipped.get()) {
				fail("Chain of InvocationInterceptors never called invocation");
			}
		}

		private void fail(String prefix) {
			String commaSeparatedInterceptorClasses = interceptors.stream().map(Object::getClass).map(
				Class::getName).collect(joining(", "));
			throw new JUnitException(prefix + ": " + commaSeparatedInterceptorClasses);
		}
	}

}

View on GitHub (pinned to f070c699a0)

Solutions

  1. Find the interceptor that issues two proceed()/skip() calls and remove the duplicate.
  2. Audit try/finally/try-with-resources blocks - call proceed() exactly once and store the result.
  3. Use a guard flag in your interceptor if conditional invocation is needed.

Example fix

// before
@Override
public void interceptTestMethod(Invocation<Void> inv, ...) throws Throwable {
    startTimer();
    inv.proceed();
    try { inv.proceed(); } finally { stopTimer(); } // double proceed
}
// after
@Override
public void interceptTestMethod(Invocation<Void> inv, ...) throws Throwable {
    startTimer();
    try { inv.proceed(); } finally { stopTimer(); }
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: compareAndSet(false, true) returning false in markInvokedOrSkipped -> fail() with prefix 'Chain of InvocationInterceptors called invocation multiple times instead of just once'. Caused by an interceptor calling invocation.proceed() twice or by both proceed() and skip() being called.

Common situations: An interceptor wraps proceed() in try/finally and calls it again in finally; an interceptor calls proceed() inside a loop; an interceptor that intentionally replays; copy-paste duplication of the proceed() call.

Related errors


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