flowable/flowable-engine · error · FlowableException

The channel payload extractor expression for the channel…

Error message

The channel payload extractor expression for the channel model with key ${channelModel.key} was empty. The payloadExtractorExpression has to be provided for a channel with an expression deserializer.

What it means

Thrown when creating an expression-based event processing pipeline: the payloadExtractorDelegateExpression on the channel model is empty. Expression-deserializer channels must supply an InboundEventPayloadExtractor to map the deserialized payload into event payload values, so registration fails.

Solutions

  1. Set channelModel.setPayloadExtractorDelegateExpression("${myPayloadExtractor}") referencing an InboundEventPayloadExtractor bean.
  2. Implement the extractor to map the deserialized object into EventPayloadInstance values.
  3. Consider the JSON/XML pipelines if you only need standard field-to-payload mapping, which provide built-in extractors.
  4. Add a pre-registration check that all expression-pipeline required fields (deserializerDelegateExpression, payloadExtractorDelegateExpression, eventTransformerDelegateExpression) are non-empty.

Example fix

// before
channelModel.setDeserializerDelegateExpression("${customDeserializer}");
// payload extractor missing

// after
channelModel.setDeserializerDelegateExpression("${customDeserializer}");
channelModel.setPayloadExtractorDelegateExpression("${customPayloadExtractor}");
Defensive patterns

Strategy: validation

Validate before calling

if (isNotBlank(channelModel.getDeserializerDelegateExpression())
        && isBlank(channelModel.getPayloadExtractorDelegateExpression())) {
    throw new IllegalArgumentException("Channel " + channelModel.getKey()
        + " needs payloadExtractorDelegateExpression for expression deserializer");
}

Type guard

null

Try / catch

try {
    eventRegistry.registerChannelModel(channelModel);
} catch (FlowableException e) {
    if (e.getMessage().contains("payload extractor expression")) {
        logger.error("Set payloadExtractorDelegateExpression on channel {}", channelModel.getKey());
    }
    throw e;
}

Prevention

When it happens

Trigger: Registering an inbound channel using the expression deserializer path where channelModel.getPayloadExtractorDelegateExpression() returns null/empty.

Common situations: Configuring deserializerDelegateExpression but overlooking payloadExtractorDelegateExpression; channel model UI field left blank; assuming a default payload extractor exists for expression channels (it does not).

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

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

        } else {
            throw new FlowableException(
                "The channel deserializer expression for the channel model with key " + channelModel.getKey()
                    + " was empty. The deserializerDelegateExpression has to be provided for a channel with an expression deserializer.");
        }

        // by default, there is not filtering of events in place
        InboundEventFilter<?> eventFilter = null;
        if (StringUtils.isNotEmpty(channelModel.getEventFilterDelegateExpression())) {
            eventFilter = resolveExpression(channelModel.getEventFilterDelegateExpression(), InboundEventFilter.class);
        }

        InboundEventTenantDetector<?> eventTenantDetector = null; // By default no multi-tenancy is applied

        InboundEventPayloadExtractor<?> eventPayloadExtractor;
        if (StringUtils.isNotEmpty(channelModel.getPayloadExtractorDelegateExpression())) {
            eventPayloadExtractor = resolveExpression(channelModel.getPayloadExtractorDelegateExpression(), InboundEventPayloadExtractor.class);
        } else {
            throw new FlowableException(
                "The channel payload extractor expression for the channel model with key " + channelModel.getKey()
                    + " was empty. The payloadExtractorExpression has to be provided for a channel with an expression deserializer.");
        }

        InboundEventTransformer eventTransformer;
        if (StringUtils.isEmpty(channelModel.getEventTransformerDelegateExpression())) {
            eventTransformer = new DefaultInboundEventTransformer();
        } else {
            eventTransformer = resolveExpression(channelModel.getEventTransformerDelegateExpression(), InboundEventTransformer.class);
        }

        InboundEventKeyDetector<?> eventKeyDetector;
        ChannelEventKeyDetection keyDetection = channelModel.getChannelEventKeyDetection();

        if (keyDetection == null) {
            throw new FlowableException("A channel key detection value is required for inbound channel " + channelModel.getKey());
        }

View on GitHub (pinned to d6d39ce1c6)