flowable/flowable-engine · error · FlowableException

Cannot find a PlanItemDefinitionExporter for

Error message

Cannot find a PlanItemDefinitionExporter for '{planItemDefinitionClass}'

What it means

During CMMN model export, each PlanItemDefinition requires a registered exporter (AbstractPlanItemDefinitionExport) keyed by the definition's canonical class name. If no exporter matches the plan item definition's class, Flowable throws this FlowableException.

Solutions

  1. Register an exporter for the custom class via the exporter registration method (registerPlanItemDefinitionExporter / put in planItemDefinitionExporters).
  2. Ensure your custom PlanItemDefinition subclasses a supported built-in type (e.g. Task, UserEventListener).
  3. Align Flowable dependency versions (converter vs engine) so all built-in exporters are present.
  4. If you don't need the element exported, remove it from the CmmnModel before conversion.

Example fix

// before
model.addPlanItemDefinition(new MyCustomTask()); // no exporter registered
// after
FlowableCmmnXmlConverter.registerPlanItemDefinitionExporter("com.acme.MyCustomTask", new MyCustomTaskExport());
Defensive patterns

Strategy: validation

Validate before calling

public static boolean hasExporter(PlanItemDefinition pid) {
    return AbstractPlanItemDefinitionExportRegistry != null
        && org.flowable.cmmn.converter.export.PlanItemDefinitionExport.class != null; // check via determineExporter reflection
}
// simpler: verify class is a known built-in type
public static boolean isSupported(PlanItemDefinition pid) {
    return pid instanceof org.flowable.cmmn.model.Task
        || pid instanceof org.flowable.cmmn.model.EventListener
        || pid instanceof org.flowable.cmmn.model.Stage
        || pid instanceof org.flowable.cmmn.model.Milestone;
}

Try / catch

try {
    CmmnXMLConverter.convertToXMLDocument(model);
} catch (FlowableException e) {
    if (e.getMessage().startsWith("Cannot find a PlanItemDefinitionExporter")) {
        logger.error("Register an exporter for the custom plan item definition before export", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling PlanItemDefinitionExport.writePlanItemDefinition (or converting a model to XML) with a custom/unregistered PlanItemDefinition subclass that was never registered via the register mechanism, or with a definition type not supported by the installed converters.

Common situations: Custom plan item definitions added via extension API without registering a matching exporter; classpath mixing Flowable versions so built-in exporters are missing; deserialized models with new definition types exported by an older converter.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanItemDefinitionExport.java:60

        addPlanItemDefinitionExport(new ExternalWorkerServiceTaskExport());
        addPlanItemDefinitionExport(new MilestoneExport());
        addPlanItemDefinitionExport(new GenericEventListenerExport());
        addPlanItemDefinitionExport(new SignalEventListenerExport());
        addPlanItemDefinitionExport(new IntentEventListenerExport());
        addPlanItemDefinitionExport(new ReactivationEventListenerExport());
        addPlanItemDefinitionExport(new TimerEventListenerExport());
        addPlanItemDefinitionExport(new UserEventListenerExport());
        addPlanItemDefinitionExport(new VariableEventListenerExport());
    }

    public static void addPlanItemDefinitionExport(AbstractPlanItemDefinitionExport exporter) {
        planItemDefinitionExporters.put(exporter.getExportablePlanItemDefinitionClass().getCanonicalName(), exporter);
    }

    public static void writePlanItemDefinition(CmmnModel model, PlanItemDefinition planItemDefinition, XMLStreamWriter xtw, CmmnXmlConverterOptions options) throws Exception {
        AbstractPlanItemDefinitionExport exporter = determineExporter(planItemDefinition);
        if (exporter == null) {
            throw new FlowableException("Cannot find a PlanItemDefinitionExporter for '" + planItemDefinition.getClass().getCanonicalName() + "'");
        }
        exporter.writePlanItemDefinition(model, planItemDefinition, xtw, options);
    }

    protected static AbstractPlanItemDefinitionExport determineExporter(PlanItemDefinition planItemDefinition) {

        AbstractPlanItemDefinitionExport exporter = null;
        Class currentPlanItemDefinitionClass = planItemDefinition.getClass();

        while (exporter == null && !currentPlanItemDefinitionClass.equals(PlanItemDefinition.class)) {
            String exporterType = currentPlanItemDefinitionClass.getCanonicalName();
            exporter = planItemDefinitionExporters.get(exporterType);

            currentPlanItemDefinitionClass = currentPlanItemDefinitionClass.getSuperclass(); // loop will stop once PlanItemDefinition is reached, so only child hierarchies will be checked
        }

        return exporter;
    }

View on GitHub (pinned to d6d39ce1c6)