flowable/flowable-engine · error · UnsupportedOperationException

CommandInvoker must be the last interceptor in the chain

Error message

CommandInvoker must be the last interceptor in the chain

What it means

DmnCommandInvoker is the terminal interceptor that executes commands; by design no interceptor may follow it. Its setNext method unconditionally throws UnsupportedOperationException('CommandInvoker must be the last interceptor in the chain') to enforce that invariant during engine command-chain configuration.

Solutions

  1. Restructure the interceptor chain so DmnCommandInvoker is added last, after all custom interceptors
  2. In your configurator, call customInterceptor.setNext(...) before wiring the invoker
  3. Remove any code path that calls setNext on the invoker (it is intentionally unsupported)
  4. If wrapping all interceptors generically, special-case DmnCommandInvoker and never append after it

Example fix

// before
invoker.setNext(customInterceptor); // throws
// after
customInterceptor.setNext(invoker); // invoker stays last
Defensive patterns

Strategy: fallback

Validate before calling

// in a configurator, assert the invoker is the final link before building
if (!lastInterceptor.equals(invoker)) throw new IllegalStateException("DmnCommandInvoker must terminate the chain");

Try / catch

try { invoker.setNext(next); } catch (UnsupportedOperationException e) { /* reorder: next.setNext(invoker) */ }

Prevention

When it happens

Trigger: Programmatically assembling a DMN engine interceptor chain and calling setNext() on the DmnCommandInvoker instance, or appending it in the middle of a chain via the engine configurator.

Common situations: Custom DmnEngineConfigurationAgenda/interceptor plugins that insert interceptors after the command invoker; copy-pasted chain-building code from process-engine configuration; middleware that wraps all interceptors generically.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/2307a28f8372370e. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/interceptor/DmnCommandInvoker.java:134

    protected void executeExecutionListenersAfterException(CommandContext commandContext, Runnable runnable, Throwable throwable) {
        if (agendaOperationExecutionListeners != null && !agendaOperationExecutionListeners.isEmpty()) {
            for (AgendaOperationExecutionListener listener : agendaOperationExecutionListeners) {
                listener.afterExecuteException(commandContext, runnable, throwable);
            }
        }
    }

    protected void executeOperation(CommandContext commandContext, Runnable runnable) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Executing agenda operation {}", runnable);
        }
        runnable.run();
    }

    @Override
    public void setNext(CommandInterceptor next) {
        throw new UnsupportedOperationException("CommandInvoker must be the last interceptor in the chain");
    }
    
}

View on GitHub (pinned to d6d39ce1c6)