{"record":{"id":"71186b6b955cb072","repo":"YunaiV/yudao-cloud","slug":"invalid-command-interceptor-chain-configuration","errorCode":null,"errorMessage":"invalid command interceptor chain configuration: {}","messagePattern":"invalid command interceptor chain configuration: (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"sql/dm/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java","lineNumber":665,"sourceCode":"\n    public abstract String getEngineCfgKey();\n\n    public abstract String getEngineScopeType();\n\n    public List<CommandInterceptor> getAdditionalDefaultCommandInterceptors() {\n        return null;\n    }\n\n    public void initCommandExecutor() {\n        if (commandExecutor == null) {\n            CommandInterceptor first = initInterceptorChain(commandInterceptors);\n            commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);\n        }\n    }\n\n    public CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {\n        if (chain == null || chain.isEmpty()) {\n            throw new FlowableException(\"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    public abstract CommandInterceptor createTransactionInterceptor();\n\n\n    public void initBeans() {\n        if (beans == null) {\n            beans = new HashMap<>();\n        }\n    }\n\n    // id generator\n    // /////////////////////////////////////////////////////////////","sourceCodeStart":647,"sourceCodeEnd":683,"githubUrl":"https://github.com/YunaiV/yudao-cloud/blob/477be9dd49ab7223a972a6abdff0684d6423dec3/sql/dm/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java#L647-L683","documentation":"Flowable builds a chain of CommandInterceptors (logging, transaction, etc.) before wrapping the first one in the CommandExecutor. initInterceptorChain throws if the configured commandInterceptors list is null or empty, because the executor would have nothing to dispatch commands through.","triggerScenarios":"Custom engine configuration calls setCommandInterceptors(emptyList()) or a configuration subclass nulls the list in getDefaultCommandInterceptors(); programmatic configuration that resets interceptors without re-adding the required defaults (transaction interceptor, logging interceptor).","commonSituations":"Copying an old custom ProcessEngineConfiguration across Flowable versions where interceptor wiring changed; a configuration that builds the interceptor list conditionally and skips all branches; Spring bean override replacing commandInterceptors with an empty collection.","solutions":["Do not blank out the list: only append via getDefaultCommandInterceptors() + add(index, customInterceptor).","If overriding, ensure at minimum a transaction interceptor and a command handler terminator remain in the chain.","Remove any setCommandInterceptors(Collections.emptyList()) or equivalent Spring property override.","Upgrade custom configuration code to the Flowable version actually on the classpath (interceptor chain layout differs across majors)."],"exampleFix":"// before\ncfg.setCommandInterceptors(new ArrayList<>()); // empty -> throws\n\n// after\nList<CommandInterceptor> chain = new ArrayList<>(cfg.getDefaultCommandInterceptors());\nchain.add(0, new MetricCommandInterceptor());\ncfg.setCommandInterceptors(chain);","handlingStrategy":"validation","validationCode":"List<CommandInterceptor> chain = cfg.getCommandInterceptors();\nif (chain == null || chain.isEmpty()) {\n    chain = cfg.getDefaultCommandInterceptors();\n    cfg.setCommandInterceptors(chain);\n}\nAssert.notEmpty(chain, \"commandInterceptors must not be empty\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never replace the interceptor list wholesale — append to getDefaultCommandInterceptors()","Add a boot-time assert on non-empty interceptor chain in custom configurations"],"tags":["flowable","command-interceptor","configuration","startup"],"backgroundTag":null,"analyzedSha":"477be9dd49ab7223a972a6abdff0684d6423dec3","analyzedAt":"2026-08-14T13:35:31.121Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}