{"record":{"id":"5463d1d29ac2f2f9","repo":"flowable/flowable-engine","slug":"the-default-bpmn-parse-handlers-should-only-suppor","errorCode":null,"errorMessage":"The default BPMN parse handlers should only support one type, but {class} supports {supportedTypes}. This is likely a programmatic error","messagePattern":"The default BPMN parse handlers should only support one type, but (.+?) supports (.+?)\\. This is likely a programmatic error","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/cfg/ProcessEngineConfigurationImpl.java","lineNumber":1911,"sourceCode":"        // Replace any default handler if the user wants to replace them\n        if (customDefaultBpmnParseHandlers != null) {\n\n            Map<Class<?>, BpmnParseHandler> customParseHandlerMap = new HashMap<>();\n            for (BpmnParseHandler bpmnParseHandler : customDefaultBpmnParseHandlers) {\n                for (Class<?> handledType : bpmnParseHandler.getHandledTypes()) {\n                    customParseHandlerMap.put(handledType, bpmnParseHandler);\n                }\n            }\n\n            for (int i = 0; i < bpmnParserHandlers.size(); i++) {\n                // All the default handlers support only one type\n                BpmnParseHandler defaultBpmnParseHandler = bpmnParserHandlers.get(i);\n                if (defaultBpmnParseHandler.getHandledTypes().size() != 1) {\n                    StringBuilder supportedTypes = new StringBuilder();\n                    for (Class<?> type : defaultBpmnParseHandler.getHandledTypes()) {\n                        supportedTypes.append(\" \").append(type.getCanonicalName()).append(\" \");\n                    }\n                    throw new FlowableException(\"The default BPMN parse handlers should only support one type, but \" + defaultBpmnParseHandler.getClass() + \" supports \" + supportedTypes\n                        + \". This is likely a programmatic error\");\n                } else {\n                    Class<?> handledType = defaultBpmnParseHandler.getHandledTypes().iterator().next();\n                    if (customParseHandlerMap.containsKey(handledType)) {\n                        BpmnParseHandler newBpmnParseHandler = customParseHandlerMap.get(handledType);\n                        logger.info(\"Replacing default BpmnParseHandler {} with {}\", defaultBpmnParseHandler.getClass().getName(), newBpmnParseHandler.getClass().getName());\n                        bpmnParserHandlers.set(i, newBpmnParseHandler);\n                    }\n                }\n            }\n        }\n\n        return bpmnParserHandlers;\n    }\n\n    public void initProcessDiagramGenerator() {\n        if (processDiagramGenerator == null) {\n            processDiagramGenerator = new DefaultProcessDiagramGenerator();","sourceCodeStart":1893,"sourceCodeEnd":1929,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/cfg/ProcessEngineConfigurationImpl.java#L1893-L1929","documentation":"During process engine configuration, ProcessEngineConfigurationImpl iterates the built-in (default) BPMN parse handlers and enforces the internal invariant that each handles exactly one BPMN element type, so custom handlers can replace defaults one-to-one. If a default handler reports getHandledTypes().size() != 1, this FlowableException is thrown listing the class and all its supported types, flagged as 'likely a programmatic error' — i.e. the shipped handler set (or an override of it) is corrupted or inconsistently extended.","triggerScenarios":"Bootstrapping the engine (buildProcessEngine) with a modified/overridden default parse-handler list where a handler's getHandledTypes() returns zero or multiple types; subclassing or repackaging BpmnParseHandler implementations into the default set; classpath shadowing where mixed flowable jar versions yield handlers built for multiple types.","commonSituations":"Custom fork of flowable-engine where a default handler was edited to handle several BPMN element types; accidentally adding custom handlers to the default handler list instead of preBpmnParseHandlers/postBpmnParseHandlers; mixed Flowable/Activiti jars on the classpath; a bad merge when upgrading Flowable versions.","solutions":["Restore each default BpmnParseHandler so getHandledTypes() returns exactly one Class — split multi-type handlers into separate handlers","Move multi-type or custom handlers out of the default handler set and register them via setPreBpmnParseHandlers()/setPostBpmnParseHandlers() instead","Check for mixed Flowable/Activiti jar versions on the classpath and align to a single flowable-engine version","If you forked the engine, diff your parse handlers against the upstream release to find the invariant violation"],"exampleFix":"// before\nclass MyHandler implements BpmnParseHandler {\n  public Collection<Class<? extends BaseElement>> getHandledTypes() {\n    return Arrays.asList(StartEvent.class, EndEvent.class); // 2 types\n  }\n}\n\n// after\nclass MyStartHandler implements BpmnParseHandler {\n  public Collection<Class<? extends BaseElement>> getHandledTypes() {\n    return Collections.singletonList(StartEvent.class); // exactly 1\n  }\n}\nclass MyEndHandler implements BpmnParseHandler {\n  public Collection<Class<? extends BaseElement>> getHandledTypes() {\n    return Collections.singletonList(EndEvent.class);\n  }\n}","handlingStrategy":"try-catch","validationCode":"// before building the engine, when supplying custom parse handlers\nList<BpmnParseHandler> handlers = processEngineConfiguration.getBpmnParseHandlers();\nfor (BpmnParseHandler h : handlers) {\n  if (h.getHandledTypes() == null || h.getHandledTypes().size() != 1) {\n    throw new IllegalStateException(h.getClass().getName()\n        + \" must handle exactly one BPMN type, got \"\n        + (h.getHandledTypes() == null ? 0 : h.getHandledTypes().size()));\n  }\n}","typeGuard":"boolean isSingleTypeHandler(BpmnParseHandler h) {\n  return h != null && h.getHandledTypes() != null && h.getHandledTypes().size() == 1;\n}","tryCatchPattern":"try {\n  return processEngineConfiguration.buildProcessEngine();\n} catch (FlowableException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"should only support one type\")) {\n    throw new IllegalStateException(\"Default BPMN parse handler set corrupted; \"\n        + \"check flowable jar versions and custom handler registration\", e);\n  }\n  throw e;\n}","preventionTips":["Never add multi-type handlers to the default handler set; use preBpmnParseHandlers/postBpmnParseHandlers","Keep one flowable-engine version on the classpath (no mixed Activiti/Flowable jars)","If forking handlers, keep the one-handled-type-per-default-handler invariant","Add a configuration smoke test that builds the engine in CI"],"tags":["flowable","bpmn","invariant","configuration","parsing"],"backgroundTag":"internal-invariant-violation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}