YunaiV/ruoyi-vue-pro · critical · FlowableException
invalid command interceptor chain configuration: {}
Error message
invalid command interceptor chain configuration: {} What it means
Flowable's initInterceptorChain requires a non-empty list of CommandInterceptors to wire into a linked list (each.setNext(next)). If commandInterceptors is null or empty the chain cannot be built and there is no first interceptor for the CommandExecutor, so it throws FlowableException. This is almost always a misconfiguration of the interceptor stack.
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 0418084e22)
Solutions
- Ensure getCommandInterceptors() returns a non-empty list that includes the required log interceptor and ends with the transaction interceptor.
- When subclassing, build on super.getCommandInterceptors() rather than replacing it entirely.
- If configuring programmatically, call setCommandInterceptors() with a populated list before buildProcessEngine().
Example fix
// before
@Override public List<CommandInterceptor> getCommandInterceptors() { return Collections.emptyList(); }
// after
@Override public List<CommandInterceptor> getCommandInterceptors() {
List<CommandInterceptor> list = new ArrayList<>(super.getCommandInterceptors());
list.add(0, new MyAuditInterceptor());
return list;
} Defensive patterns
Strategy: validation
Validate before calling
List<CommandInterceptor> is = cfg.getCommandInterceptors();
if (is == null || is.isEmpty()) throw new IllegalStateException("commandInterceptors must be non-empty"); Type guard
static boolean hasValidInterceptorChain(List<CommandInterceptor> c) { return c != null && !c.isEmpty(); } Try / catch
try { engine = cfg.buildProcessEngine(); }
catch (FlowableException e) { if (e.getMessage().contains("command interceptor chain")) { /* fix config */ } throw e; } Prevention
- Subclasses must build on super.getCommandInterceptors()
- Never set commandInterceptors to null or empty
- Unit-test custom configs that buildProcessEngine() successfully
When it happens
Trigger: A custom engine configuration overrides getCommandInterceptors()/createCommandInterceptors() and returns null or an empty list; a config subclass forgets to call super and to include the default interceptors; programmatic config that clears commandInterceptors.
Common situations: Customizing the Flowable command stack (adding metrics/audit interceptors) but returning an empty list by mistake; subclassing a process engine config and breaking the interceptor chain.
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 '{}
- Error while building ibatis SqlSessionFactory: {}
- Exception while initializing Database connection
AI-assisted analysis of YunaiV/ruoyi-vue-pro@0418084e22 (2026-08-14).
Data as JSON: /api/errors/eadcc50b1298c6df.
Report an issue: GitHub.