{"record":{"id":"c59d5f9c3ad912e0","repo":"junit-team/junit5","slug":"chain-of-invocationinterceptors-called-invocation","errorCode":null,"errorMessage":"Chain of InvocationInterceptors called invocation multiple times instead of just once: %s","messagePattern":"Chain of InvocationInterceptors called invocation multiple times instead of just once: (.+?)","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/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java#L131-L154","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n@Override\npublic void interceptTestMethod(Invocation<Void> inv, ...) throws Throwable {\n    startTimer();\n    inv.proceed();\n    try { inv.proceed(); } finally { stopTimer(); } // double proceed\n}\n// after\n@Override\npublic void interceptTestMethod(Invocation<Void> inv, ...) throws Throwable {\n    startTimer();\n    try { inv.proceed(); } finally { stopTimer(); }\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["invocation-interceptor","internal-assertion","double-invocation","extension"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}