flowable/flowable-engine · error · FlowableException

Programmatic error: task listener added to an element that i

Error message

Programmatic error: task listener added to an element that is not a human task, but a ${currentCmmnElement.getClass()}

What it means

Thrown by ExtensionElementsXMLConverter.readTaskListener when a <taskListener> extension element is encountered inside an element that is not a HumanTask. Task listeners are only valid on human tasks in CMMN, so the converter rejects them anywhere else. The message includes the actual element class to identify the misplaced listener.

Source

Thrown at modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/ExtensionElementsXMLConverter.java:372

        if (StringUtils.isNotEmpty(targetType)) {
            parameter.setTargetType(targetType);
        }

        if (StringUtils.isNotEmpty(isTransient)) {
            parameter.setTransient(Boolean.parseBoolean(isTransient));
        }
        return parameter;
    }

    protected void readTaskListener(XMLStreamReader xtr, ConversionHelper conversionHelper) throws Exception {
        BaseElement currentCmmnElement = conversionHelper.getCurrentCmmnElement(); // needs to be captured before setting the flowable listeners as this will change the current element

        FlowableListener flowableListener = ListenerXmlConverterUtil.convertToListener(xtr);
        if (flowableListener != null) {
            if (currentCmmnElement instanceof HumanTask humanTask) {
                humanTask.getTaskListeners().add(flowableListener);
            } else {
                throw new FlowableException("Programmatic error: task listener added to an element that is not a human task, but a " + currentCmmnElement.getClass());
            }
        }

        conversionHelper.setCurrentCmmnElement(flowableListener);
    }

    protected void readLifecycleListener(XMLStreamReader xtr, ConversionHelper conversionHelper) throws Exception {
        BaseElement currentCmmnElement = conversionHelper.getCurrentCmmnElement(); // needs to be captured before setting the flowable listeners as this will change the current element

        FlowableListener flowableListener = ListenerXmlConverterUtil.convertToListener(xtr);
        if (flowableListener != null) {
            if (currentCmmnElement instanceof HasLifecycleListeners lifecycleListenersElement) {
                lifecycleListenersElement.getLifecycleListeners().add(flowableListener);
            } else {
                throw new FlowableException("Programmatic error: lifecycle listener added to an element that is not a plan item definition, but a " + currentCmmnElement.getClass());
            }
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Move the taskListener inside a <humanTask>'s extensionElements, or switch the host element to a human task if a listener is required.
  2. Remove the taskListener if the host element is not a human task.
  3. Use a flowable:caseEventListener/planItem lifecycle element instead for non-human-task callbacks where supported.

Example fix

<!-- before -->
<serviceTask id="st1">
  <extensionElements>
    <flowable:taskListener event="end" class="com.acme.MyListener"/>
  </extensionElements>
</serviceTask>
<!-- after -->
<humanTask id="ht1">
  <extensionElements>
    <flowable:taskListener event="end" class="com.acme.MyListener"/>
  </extensionElements>
</humanTask>
Defensive patterns

Strategy: validation

Validate before calling

// Before conversion: every flowable:taskListener must be inside a humanTask
// XPath: //flowable:taskListener[not(ancestor::humanTask)] should return empty

Type guard

boolean canHostTaskListener(BaseElement el) {
    return el instanceof HumanTask;
}

Try / catch

try {
    cmmnModel = converter.convertToCmmnModel(stream, encoding);
} catch (FlowableException e) {
    if (e.getMessage().startsWith("Programmatic error: task listener added")) {
        // the host element class is in the message; move the listener to a HumanTask
    }
    throw e;
}

Prevention

When it happens

Trigger: A <flowable:taskListener> child of <extensionElements> whose current CMMN element is anything other than a HumanTask (e.g. a case task, service task, stage, or case element), typically from hand-edited XML or tool-generated output.

Common situations: Copying BPMN-style taskListener usage into CMMN where only HumanTask supports it; bulk XML transformations that attach listeners to the wrong elements.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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