YunaiV/yudao-cloud · error · FlowableException
invalid command interceptor chain configuration: {}
Error message
invalid command interceptor chain configuration: {} What it means
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.
Source
Thrown at sql/dm/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java:665
public abstract String getEngineCfgKey();
public abstract String getEngineScopeType();
public List<CommandInterceptor> getAdditionalDefaultCommandInterceptors() {
return null;
}
public void initCommandExecutor() {
if (commandExecutor == null) {
CommandInterceptor first = initInterceptorChain(commandInterceptors);
commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);
}
}
public CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {
if (chain == null || chain.isEmpty()) {
throw new FlowableException("invalid command interceptor chain configuration: " + chain);
}
for (int i = 0; i < chain.size() - 1; i++) {
chain.get(i).setNext(chain.get(i + 1));
}
return chain.get(0);
}
public abstract CommandInterceptor createTransactionInterceptor();
public void initBeans() {
if (beans == null) {
beans = new HashMap<>();
}
}
// id generator
// /////////////////////////////////////////////////////////////View on GitHub (pinned to 477be9dd49)
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).
Example fix
// before cfg.setCommandInterceptors(new ArrayList<>()); // empty -> throws // after List<CommandInterceptor> chain = new ArrayList<>(cfg.getDefaultCommandInterceptors()); chain.add(0, new MetricCommandInterceptor()); cfg.setCommandInterceptors(chain);
Defensive patterns
Strategy: validation
Validate before calling
List<CommandInterceptor> chain = cfg.getCommandInterceptors();
if (chain == null || chain.isEmpty()) {
chain = cfg.getDefaultCommandInterceptors();
cfg.setCommandInterceptors(chain);
}
Assert.notEmpty(chain, "commandInterceptors must not be empty"); Prevention
- Never replace the interceptor list wholesale — append to getDefaultCommandInterceptors()
- Add a boot-time assert on non-empty interceptor chain in custom configurations
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- couldn't lookup datasource from {}: {}
- DataSource or JDBC properties have to be specified in a proc
- couldn't deduct database type from database product name '{}
- Exception while initializing Database connection
- Error while building ibatis SqlSessionFactory: {}
AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14).
Data as JSON: /api/errors/71186b6b955cb072.
Report an issue: GitHub.