{"record":{"id":"b7c168c756aee0cb","repo":"flowable/flowable-engine","slug":"programmatic-error-task-listener-added-to-an-elem","errorCode":null,"errorMessage":"Programmatic error: task listener added to an element that is not a human task, but a ${currentCmmnElement.getClass()}","messagePattern":"Programmatic error: task listener added to an element that is not a human task, but a (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/ExtensionElementsXMLConverter.java","lineNumber":372,"sourceCode":"        if (StringUtils.isNotEmpty(targetType)) {\n            parameter.setTargetType(targetType);\n        }\n\n        if (StringUtils.isNotEmpty(isTransient)) {\n            parameter.setTransient(Boolean.parseBoolean(isTransient));\n        }\n        return parameter;\n    }\n\n    protected void readTaskListener(XMLStreamReader xtr, ConversionHelper conversionHelper) throws Exception {\n        BaseElement currentCmmnElement = conversionHelper.getCurrentCmmnElement(); // needs to be captured before setting the flowable listeners as this will change the current element\n\n        FlowableListener flowableListener = ListenerXmlConverterUtil.convertToListener(xtr);\n        if (flowableListener != null) {\n            if (currentCmmnElement instanceof HumanTask humanTask) {\n                humanTask.getTaskListeners().add(flowableListener);\n            } else {\n                throw new FlowableException(\"Programmatic error: task listener added to an element that is not a human task, but a \" + currentCmmnElement.getClass());\n            }\n        }\n\n        conversionHelper.setCurrentCmmnElement(flowableListener);\n    }\n\n    protected void readLifecycleListener(XMLStreamReader xtr, ConversionHelper conversionHelper) throws Exception {\n        BaseElement currentCmmnElement = conversionHelper.getCurrentCmmnElement(); // needs to be captured before setting the flowable listeners as this will change the current element\n\n        FlowableListener flowableListener = ListenerXmlConverterUtil.convertToListener(xtr);\n        if (flowableListener != null) {\n            if (currentCmmnElement instanceof HasLifecycleListeners lifecycleListenersElement) {\n                lifecycleListenersElement.getLifecycleListeners().add(flowableListener);\n            } else {\n                throw new FlowableException(\"Programmatic error: lifecycle listener added to an element that is not a plan item definition, but a \" + currentCmmnElement.getClass());\n            }\n        }\n","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/ExtensionElementsXMLConverter.java#L354-L390","documentation":"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.","triggerScenarios":"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.","commonSituations":"Copying BPMN-style taskListener usage into CMMN where only HumanTask supports it; bulk XML transformations that attach listeners to the wrong elements.","solutions":["Move the taskListener inside a <humanTask>'s extensionElements, or switch the host element to a human task if a listener is required.","Remove the taskListener if the host element is not a human task.","Use a flowable:caseEventListener/planItem lifecycle element instead for non-human-task callbacks where supported."],"exampleFix":"<!-- before -->\n<serviceTask id=\"st1\">\n  <extensionElements>\n    <flowable:taskListener event=\"end\" class=\"com.acme.MyListener\"/>\n  </extensionElements>\n</serviceTask>\n<!-- after -->\n<humanTask id=\"ht1\">\n  <extensionElements>\n    <flowable:taskListener event=\"end\" class=\"com.acme.MyListener\"/>\n  </extensionElements>\n</humanTask>","handlingStrategy":"validation","validationCode":"// Before conversion: every flowable:taskListener must be inside a humanTask\n// XPath: //flowable:taskListener[not(ancestor::humanTask)] should return empty","typeGuard":"boolean canHostTaskListener(BaseElement el) {\n    return el instanceof HumanTask;\n}","tryCatchPattern":"try {\n    cmmnModel = converter.convertToCmmnModel(stream, encoding);\n} catch (FlowableException e) {\n    if (e.getMessage().startsWith(\"Programmatic error: task listener added\")) {\n        // the host element class is in the message; move the listener to a HumanTask\n    }\n    throw e;\n}","preventionTips":["In CMMN, attach task listeners only to humanTask elements (unlike BPMN).","Lint XML so taskListener ancestors are always humanTask.","Use case event listeners or plan-item lifecycle handling for non-human-task callbacks."],"tags":["cmmn","task-listener","unsupported-element"],"backgroundTag":"invalid-argument-value","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"}