flowable/flowable-engine · error · FlowableException

DelegateExpression outbound channel model with key ${channel

Error message

DelegateExpression outbound channel model with key ${channelModel.key} resolved channel adapter delegate expression to ${channelAdapter} which is not of type ${OutboundEventChannelAdapter.class}

What it means

Thrown during channel registration when a delegate expression for an outbound channel model resolves to an object that does not implement OutboundEventChannelAdapter. The registry must hand events to a correctly typed adapter, so registration fails fast on any other type or null.

Source

Thrown at modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/pipeline/DelegateExpressionOutboundChannelModelProcessor.java:72

    public void registerChannelModel(ChannelModel channelModel, String tenantId, EventRegistry eventRegistry, EventRepositoryService eventRepositoryService,
            boolean fallbackToDefaultTenant) {
        
        if (channelModel instanceof DelegateExpressionOutboundChannelModel) {
            registerChannelModel((DelegateExpressionOutboundChannelModel) channelModel, tenantId);
        }
    }

    protected void registerChannelModel(DelegateExpressionOutboundChannelModel channelModel, String tenantId) {
        String delegateExpression = channelModel.getAdapterDelegateExpression();
        if (StringUtils.isNotEmpty(delegateExpression)) {
            VariableContainerWrapper variableContainer = new VariableContainerWrapper(Collections.emptyMap());
            variableContainer.setVariable("tenantId", tenantId);
            variableContainer.setTenantId(tenantId);
            Object channelAdapter = engineConfiguration.getExpressionManager()
                .createExpression(delegateExpression)
                .getValue(variableContainer);
            if (!(channelAdapter instanceof OutboundEventChannelAdapter)) {
                throw new FlowableException(
                    "DelegateExpression outbound channel model with key " + channelModel.getKey() + " resolved channel adapter delegate expression to "
                        + channelAdapter + " which is not of type " + OutboundEventChannelAdapter.class);
            }
            channelModel.setOutboundEventChannelAdapter(channelAdapter);
        }
    }

    @Override
    public void unregisterChannelModel(ChannelModel channelModel, String tenantId, EventRepositoryService eventRepositoryService) {
        // Nothing to do
    }

    public HasExpressionManagerEngineConfiguration getEngineConfiguration() {
        return engineConfiguration;
    }

    public void setEngineConfiguration(HasExpressionManagerEngineConfiguration engineConfiguration) {
        this.engineConfiguration = engineConfiguration;

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the referenced bean implement org.flowable.eventregistry.api.OutboundEventChannelAdapter (implement send(event) plus setEventRegistry/setOutboundChannelModel as required).
  2. Check the delegate expression string references the correct outbound adapter bean.
  3. Inspect the resolved object in the message and fix the Spring/CDI wiring.
  4. For inbound traffic use the inbound processor/channel model instead.

Example fix

// before
channelModel.setChannelAdapterDelegateExpression("${inboundAdapter}"); // resolves to an InboundEventChannelAdapter

// after
channelModel.setChannelAdapterDelegateExpression("${kafkaOutboundAdapter}"); // bean implements OutboundEventChannelAdapter
Defensive patterns

Strategy: validation

Validate before calling

Object adapter = applicationContext.getBean(exprName);
if (!(adapter instanceof org.flowable.eventregistry.api.OutboundEventChannelAdapter)) {
    throw new IllegalStateException("Bean " + exprName + " is not an OutboundEventChannelAdapter");
}

Type guard

boolean isValidOutboundAdapter(Object o) {
    return o instanceof org.flowable.eventregistry.api.OutboundEventChannelAdapter;
}

Try / catch

try {
    eventRegistry.registerChannelModel(channelModel);
} catch (FlowableException e) {
    if (e.getMessage().contains("not of type")) {
        logger.error("Outbound adapter bean has wrong type: {}", e.getMessage());
    }
    throw e;
}

Prevention

When it happens

Trigger: Registering an outbound channel model whose channelAdapterDelegateExpression resolves to a non-OutboundEventChannelAdapter object (wrong bean, wrong class, or null) via the expression manager.

Common situations: Wiring an inbound adapter into an outbound channel; bean class refactored to drop the interface; wrong bean name in the expression; bean factory returning null.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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