junit-team/junit5 · error · JUnitException

Chain of InvocationInterceptors never called invocation: <in

Error message

Chain of InvocationInterceptors never called invocation: <interceptors>

What it means

Thrown by InvocationInterceptorChain.ValidatingInvocation.verifyInvokedAtLeastOnce() after the interceptor chain completes if none of the interceptors called proceed() or skip() on the supplied Invocation. The engine wraps every interceptable call (test, lifecycle, constructor) in a validating chain to guarantee the actual invocation is reached; an interceptor that silently returns without delegating breaks execution.

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 956246301e)

Solutions

  1. In every interceptXxx override, ensure the code path calls invocation.proceed() (to run the underlying method) or invocation.skip() (to skip it) exactly once.
  2. Audit interceptor logic branches — every branch must delegate; a missing proceed() on an early-return path is the usual culprit.
  3. If the interceptor is meant to replace the invocation, still call proceed() on a no-op Invocation rather than returning silently.

Example fix

// before
public <T> T interceptTestClassConstructor(Invocation<T> invocation, ... ) {
    log("constructing");
    return null; // never called proceed() -> JUnitException
}
// after
public <T> T interceptTestClassConstructor(Invocation<T> invocation, ...) throws Throwable {
    log("constructing");
    return invocation.proceed();
}
Defensive patterns

Strategy: validation

Validate before calling

// Statically inspect each intercept method delegates
// (manual review): every branch in interceptXxx must call invocation.proceed() or invocation.skip().
// No runtime pre-check is possible since the engine invokes the chain internally.

Prevention

When it happens

Trigger: Registering an InvocationInterceptor whose interceptXxx method returns a value without ever calling invocation.proceed() (or invocation.skip()), for any of the interceptable methods (interceptTestFactoryMethod, interceptTestMethod, interceptBeforeAllMethod, etc.).

Common situations: A custom 'short-circuit' interceptor meant to skip a test but written to return a default instead of calling invocation.skip(); a logging interceptor that forgets to forward to proceed(); an interceptor that throws and is caught internally without re-delegating.

Related errors


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