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
- In every interceptXxx override, ensure the code path calls invocation.proceed() (to run the underlying method) or invocation.skip() (to skip it) exactly once.
- Audit interceptor logic branches — every branch must delegate; a missing proceed() on an early-return path is the usual culprit.
- 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
- In every interceptXxx override, ensure invocation.proceed() (or skip()) is reached on every code path.
- Write a unit test that registers the interceptor and asserts the underlying method actually executes.
- Avoid returning a default value from an interceptor without delegating.
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
- Failed to publish path
- Failed to create output directory
- The following TestInstanceFactory extensions were registered
- TestInstanceFactory [%s] failed to instantiate test class [%
- TestInstanceFactory [%s] failed to return an instance of [%s
AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04).
Data as JSON: /data/errors/a9fce0bc9cbb86b6.json.
Report an issue: GitHub.