{"record":{"id":"33c422fb7ece8007","repo":"flowable/flowable-engine","slug":"commandinvoker-must-be-the-last-interceptor-in-the","errorCode":null,"errorMessage":"CommandInvoker must be the last interceptor in the chain","messagePattern":"CommandInvoker must be the last interceptor in the chain","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/interceptor/CmmnCommandInvoker.java","lineNumber":208,"sourceCode":"                }\n                \n                agenda.planEvaluateCriteriaOperation(caseInstanceId, true);\n            }\n\n            involvedCaseInstanceIds.clear(); // Clearing after scheduling the evaluation. If anything changes, new operations will add ids again.\n            executeOperations(commandContext, false); // false -> here, we're past the regular operation loop. Any operation now that is a no-op should not reschedule a new evaluation\n\n            // If new involvedCaseInstanceIds have new entries, this means the evaluation has triggered new operations and data has changed.\n            // Need to retrigger the evaluations to make sure no new things can fire now.\n            if (!involvedCaseInstanceIds.isEmpty()) {\n                evaluateUntilStable(commandContext, allHandledCaseInstanceIds);\n            }\n        }\n    }\n\n    @Override\n    public void setNext(CommandInterceptor next) {\n        throw new UnsupportedOperationException(\"CommandInvoker must be the last interceptor in the chain\");\n    }\n\n    public AgendaOperationRunner getAgendaOperationRunner() {\n        return agendaOperationRunner;\n    }\n\n    public void setAgendaOperationRunner(AgendaOperationRunner agendaOperationRunner) {\n        this.agendaOperationRunner = agendaOperationRunner;\n    }\n\n}\n","sourceCodeStart":190,"sourceCodeEnd":220,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/interceptor/CmmnCommandInvoker.java#L190-L220","documentation":"CmmnCommandInvoker.setNext() unconditionally throws UnsupportedOperationException because the command invoker must be the terminal interceptor of the command chain — nothing may come after it. This is a deliberate invariant-enforcement guard used while configuring the CMMN engine's interceptor stack.","triggerScenarios":"Programmatically building a custom interceptor chain and calling setNext(cmmnCommandInvoker-like interceptor) with the CmmnCommandInvoker as the 'next' target, i.e. attempting to append an interceptor after the invoker.","commonSituations":"Custom CmmnEngineConfiguration interceptor setups; copying ProcessEngineConfiguration chain-building code to the CMMN engine and wiring the invoker in the wrong position.","solutions":["Register CmmnCommandInvoker as the LAST interceptor: call setNext on preceding interceptors pointing toward the invoker, never setNext ON the invoker","Use configuration defaults instead of manual chain wiring if possible","Reorder your interceptor list so the invoker is at the end"],"exampleFix":"// before\ncustomInterceptor.setNext(commandInvoker); // throws: nothing after invoker\n// after\ncommandInvoker.setNext(customInterceptor); // wrong direction too — instead:\n// beforeInterceptor.setNext(commandInvoker); // invoker must be last\n","handlingStrategy":"validation","validationCode":"if (interceptor instanceof CmmnCommandInvoker) {\n    throw new IllegalStateException(\"CmmnCommandInvoker must be the last interceptor: never call setNext on it\");\n}","typeGuard":"boolean isCommandInvoker(CommandInterceptor interceptor) {\n    return interceptor instanceof CmmnCommandInvoker;\n}","tryCatchPattern":"try {\n    previousInterceptor.setNext(invoker); // invoker as target, not as caller\n} catch (UnsupportedOperationException e) {\n    // you tried to append after the invoker — reorder so the invoker is last\n}","preventionTips":["Build the interceptor chain front-to-back, ending with CmmnCommandInvoker","Never call setNext() on the command invoker instance","Prefer relying on CmmnEngineConfiguration's default interceptor setup"],"tags":["flowable","cmmn","interceptor","configuration","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}