flowable/flowable-engine · error · RuntimeException

Could not generate DI: boundaryEvent

Error message

Could not generate DI: boundaryEvent '${boundaryEvent.getId()}' has no attachedToRef

What it means

BpmnAutoLayout generates BPMN DI (diagram interchange) coordinates. For a boundary event it must know which activity the event is attached to; if the boundary event has neither attachedToRefId nor attachedToRef, layout cannot place it and throws RuntimeException.

Solutions

  1. Set boundaryEvent.setAttachedToRefId("<activityId>") before calling layout()
  2. Or set boundaryEvent.setAttachedToRef(activity) with the target BaseElement
  3. Verify the attached-to activity id exists in the model (it must also be laid out)

Example fix

// before
BoundaryEvent be = new BoundaryEvent(); be.setId("be1");
process.addFlowElement(be);
// after
BoundaryEvent be = new BoundaryEvent(); be.setId("be1");
be.setAttachedToRefId("task1");
process.addFlowElement(be);
Defensive patterns

Strategy: try-catch

Try / catch

try { layout.layout(); } catch (RuntimeException e) { if (e.getMessage().contains("has no attachedToRef")) { /* fix model */ } }

Prevention

When it happens

Trigger: Programmatically building a BpmnModel with a BoundaryEvent and calling BpmnAutoLayout.layout() without setting attachedToRef/attachedToRefId on the boundary event.

Common situations: Code-generated models where the attachment link was skipped; models built from incomplete data; boundary events copied without their parent reference.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-bpmn-layout/src/main/java/org/flowable/bpmn/BpmnAutoLayout.java:247

        generatedVertices.put(flowElement.getId(), subProcessVertex);
    }

    protected void handleBoundaryEvents() {
        for (BoundaryEvent boundaryEvent : boundaryEvents) {
            mxGeometry geometry = new mxGeometry(0.8, 1.0, eventSize, eventSize);
            geometry.setOffset(new mxPoint(-(eventSize / 2), -(eventSize / 2)));
            geometry.setRelative(true);
            mxCell boundaryPort = new mxCell(null, geometry, "shape=ellipse;perimeter=ellipsePerimeter");
            boundaryPort.setId("boundary-event-" + boundaryEvent.getId());
            boundaryPort.setVertex(true);

            Object portParent = null;
            if (boundaryEvent.getAttachedToRefId() != null) {
                portParent = generatedVertices.get(boundaryEvent.getAttachedToRefId());
            } else if (boundaryEvent.getAttachedToRef() != null) {
                portParent = generatedVertices.get(boundaryEvent.getAttachedToRef().getId());
            } else {
                throw new RuntimeException("Could not generate DI: boundaryEvent '" + boundaryEvent.getId() + "' has no attachedToRef");
            }

            graph.addCell(boundaryPort, portParent);
            generatedVertices.put(boundaryEvent.getId(), boundaryPort);
        }
    }

    protected void handleSequenceFlow() {

        Hashtable<String, Object> edgeStyle = new Hashtable<>();
        edgeStyle.put(mxConstants.STYLE_ORTHOGONAL, true);
        edgeStyle.put(mxConstants.STYLE_EDGE, mxEdgeStyle.ElbowConnector);
        edgeStyle.put(mxConstants.STYLE_ENTRY_X, 0.0);
        edgeStyle.put(mxConstants.STYLE_ENTRY_Y, 0.5);
        graph.getStylesheet().putCellStyle(STYLE_SEQUENCEFLOW, edgeStyle);

        Hashtable<String, Object> boundaryEdgeStyle = new Hashtable<>();
        boundaryEdgeStyle.put(mxConstants.STYLE_EXIT_X, 0.5);

View on GitHub (pinned to d6d39ce1c6)