{"record":{"id":"c64ad0ed3beab0c9","repo":"flowable/flowable-engine","slug":"invalid-command-interceptor-chain-configuration-c64ad0","errorCode":null,"errorMessage":"invalid command interceptor chain configuration: ${chain}","messagePattern":"invalid command interceptor chain configuration: (.+?)","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"critical","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/ProcessEngineConfigurationImpl.java","lineNumber":688,"sourceCode":"        CommandInterceptor transactionInterceptor = createTransactionInterceptor();\n        if (transactionInterceptor != null) {\n            interceptors.add(transactionInterceptor);\n        }\n\n        interceptors.add(new CommandContextInterceptor(commandContextFactory, this));\n        return interceptors;\n    }\n\n    protected void initCommandExecutor() {\n        if (commandExecutor == null) {\n            CommandInterceptor first = initInterceptorChain(commandInterceptors);\n            commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);\n        }\n    }\n\n    protected CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {\n        if (chain == null || chain.isEmpty()) {\n            throw new ActivitiException(\"invalid command interceptor chain configuration: \" + chain);\n        }\n        for (int i = 0; i < chain.size() - 1; i++) {\n            chain.get(i).setNext(chain.get(i + 1));\n        }\n        return chain.get(0);\n    }\n\n    protected abstract CommandInterceptor createTransactionInterceptor();\n\n    // services /////////////////////////////////////////////////////////////////\n\n    protected void initServices() {\n        initService(repositoryService);\n        initService(runtimeService);\n        initService(historyService);\n        initService(identityService);\n        initService(taskService);\n        initService(formService);","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/ProcessEngineConfigurationImpl.java#L670-L706","documentation":"initInterceptorChain links the configured CommandInterceptor list into a chain and returns its head. If the list is null or empty there is no first interceptor, so the engine cannot build a CommandExecutor and throws this ActivitiException. This indicates the interceptor configuration was not initialized correctly.","triggerScenarios":"Custom process engine configuration that overrides/clears defaultCommandInterceptors or customPreCommandInterceptors/customPostCommandInterceptors such that resolveInterceptors produces an empty list passed to initInterceptorChain during engine init.","commonSituations":"Subclassing ProcessEngineConfigurationImpl and forgetting to call super or to populate defaultCommandInterceptors; filtering logic removing all interceptors; misconfigured custom interceptor lists.","solutions":["Ensure defaultCommandInterceptors is populated before initCommandExecutors runs (call the default init methods in your subclass)","Do not return an empty list from custom pre/post interceptor configuration — add at least the standard chain (log, transaction, command context interceptors)","If subclassing, override initInterceptorChain only to extend, never to replace with an empty chain"],"exampleFix":"// before\n@Override\nprotected List<CommandInterceptor> getDefaultCommandInterceptors() {\n  return customInterceptors; // may be empty\n}\n// after\n@Override\nprotected List<CommandInterceptor> getDefaultCommandInterceptors() {\n  List<CommandInterceptor> list = super.getDefaultCommandInterceptors();\n  list.addAll(customInterceptors);\n  return list;\n}","handlingStrategy":"validation","validationCode":"List<CommandInterceptor> chain = cfg.getDefaultCommandInterceptors();\nif (chain == null || chain.isEmpty()) {\n  throw new IllegalStateException(\"Command interceptor chain must not be empty\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  processEngine = cfg.buildProcessEngine();\n} catch (ActivitiException e) {\n  if (e.getMessage().startsWith(\"invalid command interceptor chain\")) {\n    throw new ConfigurationException(\"Populate defaultCommandInterceptors before building the engine\", e);\n  } else throw e;\n}","preventionTips":["Call the standard init methods (initCommandInterceptors/initCommandExecutors) in subclasses","Never replace the default chain with an empty list; only prepend/append","Use customPreCommandInterceptors/customPostCommandInterceptors for additions","Log the resolved chain length at startup in custom configurations"],"tags":["activiti","configuration","command-chain"],"backgroundTag":"internal-invariant-violation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}