flowable/flowable-engine · critical · ActivitiException
invalid command interceptor chain configuration
Error message
invalid command interceptor chain configuration: ${chain} What it means
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.
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
Example fix
// before
@Override
protected List<CommandInterceptor> getDefaultCommandInterceptors() {
return customInterceptors; // may be empty
}
// after
@Override
protected List<CommandInterceptor> getDefaultCommandInterceptors() {
List<CommandInterceptor> list = super.getDefaultCommandInterceptors();
list.addAll(customInterceptors);
return list;
} Defensive patterns
Strategy: validation
Validate before calling
List<CommandInterceptor> chain = cfg.getDefaultCommandInterceptors();
if (chain == null || chain.isEmpty()) {
throw new IllegalStateException("Command interceptor chain must not be empty");
} Try / catch
try {
processEngine = cfg.buildProcessEngine();
} catch (ActivitiException e) {
if (e.getMessage().startsWith("invalid command interceptor chain")) {
throw new ConfigurationException("Populate defaultCommandInterceptors before building the engine", e);
} else throw e;
} Prevention
- 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
When it happens
Trigger: Custom process engine configuration that overrides/clears defaultCommandInterceptors or customPreCommandInterceptors/customPostCommandInterceptors such that resolveInterceptors produces an empty list passed to initInterceptorChain during engine init.
Common situations: Subclassing ProcessEngineConfigurationImpl and forgetting to call super or to populate defaultCommandInterceptors; filtering logic removing all interceptors; misconfigured custom interceptor lists.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Cannot set JPA variable: " + EntityManagerSession.class + "…
- couldn't deduct database type from database product name
- couldn't lookup datasource from
- DataSource or JDBC properties have to be specified in a…
- failedJobRetryTimeCycle has wrong format:" +…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/c64ad0ed3beab0c9.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/ProcessEngineConfigurationImpl.java:688
CommandInterceptor transactionInterceptor = createTransactionInterceptor();
if (transactionInterceptor != null) {
interceptors.add(transactionInterceptor);
}
interceptors.add(new CommandContextInterceptor(commandContextFactory, this));
return interceptors;
}
protected void initCommandExecutor() {
if (commandExecutor == null) {
CommandInterceptor first = initInterceptorChain(commandInterceptors);
commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);
}
}
protected CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {
if (chain == null || chain.isEmpty()) {
throw new ActivitiException("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);
}
protected abstract CommandInterceptor createTransactionInterceptor();
// services /////////////////////////////////////////////////////////////////
protected void initServices() {
initService(repositoryService);
initService(runtimeService);
initService(historyService);
initService(identityService);
initService(taskService);
initService(formService);View on GitHub (pinned to d6d39ce1c6)