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
- Find the interceptor that issues two proceed()/skip() calls and remove the duplicate.
- Audit try/finally/try-with-resources blocks - call proceed() exactly once and store the result.
- 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
- Call invocation.proceed() exactly once per intercept call - store its result if needed.
- Never put proceed() in a finally that also runs on the happy path.
- Avoid loops that replay the invocation.
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
- Chain of InvocationInterceptors never called invocation: %s
- Unsupported AnnotatedElement type for @TempDir: %s
- The following TestInstanceFactory extensions were registered
- TestInstanceFactory [%s] failed to instantiate test class [%
- No ParameterResolver registered for parameter [%s] in %s [%s
AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11).
Data as JSON: /api/errors/c59d5f9c3ad912e0.
Report an issue: GitHub.