{"record":{"id":"c712c37098f78a34","repo":"flowable/flowable-engine","slug":"compensation-activity-could-not-be-found-or-it-is","errorCode":null,"errorMessage":"Compensation activity could not be found (or it is missing 'isForCompensation=\"true\"') for ${executionEntity}","messagePattern":"Compensation activity could not be found \\(or it is missing 'isForCompensation=\"true\"'\\) for (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/BoundaryCompensateEventActivityBehavior.java","lineNumber":78,"sourceCode":"        Activity compensationActivity = null;\n        List<Association> associations = process.findAssociationsWithSourceRefRecursive(boundaryEvent.getId());\n        for (Association association : associations) {\n            sourceActivity = boundaryEvent.getAttachedToRef();\n            FlowElement targetElement = process.getFlowElement(association.getTargetRef(), true);\n            if (targetElement instanceof Activity activity) {\n                if (activity.isForCompensation()) {\n                    compensationActivity = activity;\n                    break;\n                }\n            }\n        }\n        \n        if (sourceActivity == null) {\n            throw new FlowableException(\"Parent activity for boundary compensation event could not be found for \" + executionEntity);\n        }\n\n        if (compensationActivity == null) {\n            throw new FlowableException(\"Compensation activity could not be found (or it is missing 'isForCompensation=\\\"true\\\"') for \" + executionEntity);\n        }\n\n        // find SubProcess or Process instance execution\n        ExecutionEntity scopeExecution = null;\n        ExecutionEntity parentExecution = executionEntity.getParent();\n        while (scopeExecution == null && parentExecution != null) {\n            if (parentExecution.getCurrentFlowElement() instanceof SubProcess) {\n                scopeExecution = parentExecution;\n\n            } else if (parentExecution.isProcessInstanceType()) {\n                scopeExecution = parentExecution;\n            } else {\n                parentExecution = parentExecution.getParent();\n            }\n        }\n\n        if (scopeExecution == null) {\n            throw new FlowableException(\"Could not find a scope execution for compensation boundary event \" + boundaryEvent.getId() + \" for \" + executionEntity);","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/BoundaryCompensateEventActivityBehavior.java#L60-L96","documentation":"Flowable throws this when the compensation boundary event's association points to an activity, but no target activity marked isForCompensation=\"true\" can be found. execute() walks the associations to find compensationActivity; it stays null when the association target is missing or the compensation handler lacks the isForCompensation attribute, which Flowable requires to treat it as a compensation handler.","triggerScenarios":"A boundary compensate event has an association whose targetRef activity is not flagged isForCompensation=\"true\", the targetRef id does not exist in the model, or the association points at a plain task/service task instead of a compensation handler.","commonSituations":"Forgetting isForCompensation=\"true\" on the compensation task in hand-written XML; the modeler generating a compensation event but the handler not being marked; typos in the handler's activity id; modeling compensation without a proper compensation handler activity.","solutions":["Add isForCompensation=\"true\" to the activity referenced by the association's targetRef, then redeploy.","Confirm the association's targetRef matches an existing activity id in the process.","Use a serviceTask/task element designed for compensation and ensure it is only reachable via compensation (no incoming sequence flows).","Re-export the diagram from the Flowable modeler to regenerate correct association/attributes."],"exampleFix":"// before\n<bpmn:serviceTask id=\"undoTask\" flowable:class=\"com.acme.UndoDelegate\"/>\n\n// after\n<bpmn:serviceTask id=\"undoTask\" isForCompensation=\"true\" flowable:class=\"com.acme.UndoDelegate\"/>","handlingStrategy":"validation","validationCode":"boolean hasMarkedCompensationHandler(BpmnModel model, String boundaryEventId) {\n    Process process = model.getMainProcess();\n    return process.findAssociationsWithSourceRefRecursive(boundaryEventId).stream()\n        .map(a -> model.getFlowElement(a.getTargetRef()))\n        .filter(Objects::nonNull)\n        .anyMatch(fe -> fe instanceof Activity && Boolean.TRUE.equals(((Activity) fe).isForCompensation()));\n}","typeGuard":null,"tryCatchPattern":"try {\n    runtimeService.startProcessInstanceByKey(\"myProcess\");\n} catch (FlowableException e) {\n    if (e.getMessage().contains(\"isForCompensation\")) {\n        // handler not marked: add isForCompensation=\"true\" and redeploy\n    }\n}","preventionTips":["Always set isForCompensation=\"true\" on activities used as compensation handlers.","Keep compensation handlers without incoming sequence flows so they are only invoked by compensation.","Validate the deployed BpmnModel programmatically in integration tests before go-live."],"tags":["flowable","bpmn","compensation","isforcompensation","model-validation"],"backgroundTag":"schema-validation-failed","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"}