{"id":"a9fce0bc9cbb86b6","repo":"junit-team/junit5","slug":"chain-of-invocationinterceptors-never-called-invoc","errorCode":null,"errorMessage":"Chain of InvocationInterceptors never called invocation: <interceptors>","messagePattern":"Chain of InvocationInterceptors never called invocation: <interceptors>","errorType":"exception","errorClass":"JUnitException","httpStatus":null,"severity":"error","filePath":"junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java","lineNumber":149,"sourceCode":"\t\t\tdelegate.skip();\n\t\t}\n\n\t\tprivate void markInvokedOrSkipped() {\n\t\t\tif (!invokedOrSkipped.compareAndSet(false, true)) {\n\t\t\t\tfail(\"Chain of InvocationInterceptors called invocation multiple times instead of just once\");\n\t\t\t}\n\t\t}\n\n\t\tvoid verifyInvokedAtLeastOnce() {\n\t\t\tif (!invokedOrSkipped.get()) {\n\t\t\t\tfail(\"Chain of InvocationInterceptors never called invocation\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void fail(String prefix) {\n\t\t\tString commaSeparatedInterceptorClasses = interceptors.stream().map(Object::getClass).map(\n\t\t\t\tClass::getName).collect(joining(\", \"));\n\t\t\tthrow new JUnitException(prefix + \": \" + commaSeparatedInterceptorClasses);\n\t\t}\n\t}\n\n}\n","sourceCodeStart":131,"sourceCodeEnd":154,"githubUrl":"https://github.com/junit-team/junit5/blob/956246301e03d757539f7b76dd5a91f298637563/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java#L131-L154","documentation":"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.","triggerScenarios":"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.).","commonSituations":"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.","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."],"exampleFix":"// before\npublic <T> T interceptTestClassConstructor(Invocation<T> invocation, ... ) {\n    log(\"constructing\");\n    return null; // never called proceed() -> JUnitException\n}\n// after\npublic <T> T interceptTestClassConstructor(Invocation<T> invocation, ...) throws Throwable {\n    log(\"constructing\");\n    return invocation.proceed();\n}","handlingStrategy":"validation","validationCode":"// Statically inspect each intercept method delegates\n// (manual review): every branch in interceptXxx must call invocation.proceed() or invocation.skip().\n// No runtime pre-check is possible since the engine invokes the chain internally.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["junit-jupiter","extension","invocation-interceptor","internal"],"analyzedSha":"956246301e03d757539f7b76dd5a91f298637563","analyzedAt":"2026-08-04T19:25:17.049Z","schemaVersion":2}