{"record":{"id":"b783d80a7edf542f","repo":"flowable/flowable-engine","slug":"delegateexpression-outbound-channel-model-with-key","errorCode":null,"errorMessage":"DelegateExpression outbound channel model with key ${channelModel.key} resolved channel adapter delegate expression to ${channelAdapter} which is not of type ${OutboundEventChannelAdapter.class}","messagePattern":"DelegateExpression outbound channel model with key (.+?) resolved channel adapter delegate expression to (.+?) which is not of type (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/pipeline/DelegateExpressionOutboundChannelModelProcessor.java","lineNumber":72,"sourceCode":"    public void registerChannelModel(ChannelModel channelModel, String tenantId, EventRegistry eventRegistry, EventRepositoryService eventRepositoryService,\n            boolean fallbackToDefaultTenant) {\n        \n        if (channelModel instanceof DelegateExpressionOutboundChannelModel) {\n            registerChannelModel((DelegateExpressionOutboundChannelModel) channelModel, tenantId);\n        }\n    }\n\n    protected void registerChannelModel(DelegateExpressionOutboundChannelModel channelModel, String tenantId) {\n        String delegateExpression = channelModel.getAdapterDelegateExpression();\n        if (StringUtils.isNotEmpty(delegateExpression)) {\n            VariableContainerWrapper variableContainer = new VariableContainerWrapper(Collections.emptyMap());\n            variableContainer.setVariable(\"tenantId\", tenantId);\n            variableContainer.setTenantId(tenantId);\n            Object channelAdapter = engineConfiguration.getExpressionManager()\n                .createExpression(delegateExpression)\n                .getValue(variableContainer);\n            if (!(channelAdapter instanceof OutboundEventChannelAdapter)) {\n                throw new FlowableException(\n                    \"DelegateExpression outbound channel model with key \" + channelModel.getKey() + \" resolved channel adapter delegate expression to \"\n                        + channelAdapter + \" which is not of type \" + OutboundEventChannelAdapter.class);\n            }\n            channelModel.setOutboundEventChannelAdapter(channelAdapter);\n        }\n    }\n\n    @Override\n    public void unregisterChannelModel(ChannelModel channelModel, String tenantId, EventRepositoryService eventRepositoryService) {\n        // Nothing to do\n    }\n\n    public HasExpressionManagerEngineConfiguration getEngineConfiguration() {\n        return engineConfiguration;\n    }\n\n    public void setEngineConfiguration(HasExpressionManagerEngineConfiguration engineConfiguration) {\n        this.engineConfiguration = engineConfiguration;","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/pipeline/DelegateExpressionOutboundChannelModelProcessor.java#L54-L90","documentation":"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.","triggerScenarios":"Registering an outbound channel model whose channelAdapterDelegateExpression resolves to a non-OutboundEventChannelAdapter object (wrong bean, wrong class, or null) via the expression manager.","commonSituations":"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.","solutions":["Make the referenced bean implement org.flowable.eventregistry.api.OutboundEventChannelAdapter (implement send(event) plus setEventRegistry/setOutboundChannelModel as required).","Check the delegate expression string references the correct outbound adapter bean.","Inspect the resolved object in the message and fix the Spring/CDI wiring.","For inbound traffic use the inbound processor/channel model instead."],"exampleFix":"// before\nchannelModel.setChannelAdapterDelegateExpression(\"${inboundAdapter}\"); // resolves to an InboundEventChannelAdapter\n\n// after\nchannelModel.setChannelAdapterDelegateExpression(\"${kafkaOutboundAdapter}\"); // bean implements OutboundEventChannelAdapter","handlingStrategy":"validation","validationCode":"Object adapter = applicationContext.getBean(exprName);\nif (!(adapter instanceof org.flowable.eventregistry.api.OutboundEventChannelAdapter)) {\n    throw new IllegalStateException(\"Bean \" + exprName + \" is not an OutboundEventChannelAdapter\");\n}","typeGuard":"boolean isValidOutboundAdapter(Object o) {\n    return o instanceof org.flowable.eventregistry.api.OutboundEventChannelAdapter;\n}","tryCatchPattern":"try {\n    eventRegistry.registerChannelModel(channelModel);\n} catch (FlowableException e) {\n    if (e.getMessage().contains(\"not of type\")) {\n        logger.error(\"Outbound adapter bean has wrong type: {}\", e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Declare outbound adapter beans with OutboundEventChannelAdapter as the declared type","Do not reuse one adapter class for both inbound and outbound channels","Add an integration test registering all channel models from your deployment resources"],"tags":["flowable","event-registry","type-mismatch","spring-bean"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}