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

  1. Do not blank out the list: only append via getDefaultCommandInterceptors() + add(index, customInterceptor).
  2. If overriding, ensure at minimum a transaction interceptor and a command handler terminator remain in the chain.
  3. Remove any setCommandInterceptors(Collections.emptyList()) or equivalent Spring property override.
  4. 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

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


AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14). Data as JSON: /api/errors/71186b6b955cb072. Report an issue: GitHub.