{"record":{"id":"abddafdb5513e153","repo":"Activiti/Activiti","slug":"programmatic-error-should-not-be-used-for-anythi","errorCode":null,"errorMessage":"Programmatic error:  should not be used for anything else than a boundary event","messagePattern":"Programmatic error:  should not be used for anything else than a boundary event","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"critical","filePath":"activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/BoundaryTimerEventActivityBehavior.java","lineNumber":47,"sourceCode":"/**\n\n */\npublic class BoundaryTimerEventActivityBehavior extends BoundaryEventActivityBehavior {\n\n    private static final long serialVersionUID = 1L;\n\n    protected TimerEventDefinition timerEventDefinition;\n\n    public BoundaryTimerEventActivityBehavior(TimerEventDefinition timerEventDefinition, boolean interrupting) {\n        super(interrupting);\n        this.timerEventDefinition = timerEventDefinition;\n    }\n\n    @Override\n    public void execute(DelegateExecution execution) {\n        ExecutionEntity executionEntity = (ExecutionEntity) execution;\n        if (!(execution.getCurrentFlowElement() instanceof BoundaryEvent)) {\n            throw new ActivitiException(\n                \"Programmatic error: \" + this.getClass() + \" should not be used for anything else than a boundary event\"\n            );\n        }\n\n        JobManager jobManager = Context.getCommandContext().getJobManager();\n        TimerJobEntity timerJob = jobManager.createTimerJob(\n            timerEventDefinition,\n            interrupting,\n            executionEntity,\n            TriggerTimerEventJobHandler.TYPE,\n            TimerEventHandler.createConfiguration(\n                execution.getCurrentActivityId(),\n                timerEventDefinition.getEndDate(),\n                timerEventDefinition.getCalendarName()\n            )\n        );\n        if (timerJob != null) {\n            jobManager.scheduleTimerJob(timerJob);","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/Activiti/Activiti/blob/56435b1a97deeafdc09dd40074b056c89fba5a8a/activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/BoundaryTimerEventActivityBehavior.java#L29-L65","documentation":"BoundaryTimerEventActivityBehavior.execute asserts that the current flow element is a BoundaryEvent before creating a timer job. If any other flow element (start event, intermediate catch, activity, etc.) is mapped to this behavior, the precondition fails and this 'Programmatic error' ActivitiException is thrown. It signals behavior-to-element wiring is wrong, not user input.","triggerScenarios":"execute() runs with execution.getCurrentFlowElement() not instanceof BoundaryEvent — caused by a custom BpmnParseHandler or ActivityBehaviorFactory override that assigns BoundaryTimerEventActivityBehavior to a non-boundary timer element (e.g. intermediateCatchEvent or startEvent with a TimeCycle/TimeDuration).","commonSituations":"Custom process engine configurators changing default behavior bindings; copy-paste of behavior assignments in custom parse handlers; model refactor that turned a boundary timer into a standalone timer event while old custom bindings remained deployed.","solutions":["Review custom ActivityBehaviorFactory/BpmnParseHandler overrides and ensure BoundaryTimerEventActivityBehavior is only returned for BoundaryEvent elements.","Check which element id the failing execution was on (from the execution's current flow element) and verify its type in the deployed BPMN XML.","Redeploy the process definition after fixing the model so the correct behavior mapping applies.","If you truly need timer behavior on a non-boundary element, use IntermediateCatchEventActivityBehavior or the timer start-event behavior instead."],"exampleFix":"// before\npublic ActivityBehavior createIntermediateCatchEventBehavior(IntermediateCatchEvent catchEvent) {\n    return new BoundaryTimerEventActivityBehavior(); // wrong binding\n}\n\n// after\npublic ActivityBehavior createIntermediateCatchEventBehavior(IntermediateCatchEvent catchEvent) {\n    return new IntermediateCatchEventActivityBehavior();\n}","handlingStrategy":"type-guard","validationCode":"if (!(flowElement instanceof BoundaryEvent)) {\n    throw new IllegalStateException(\"BoundaryTimerEventActivityBehavior bound to non-boundary element: \" + flowElement.getId());\n}","typeGuard":"boolean isBoundaryTimerBinding(FlowElement el) {\n    return el instanceof BoundaryEvent\n        && el.getEventDefinitions().stream().anyMatch(d -> d instanceof TimerEventDefinition);\n}","tryCatchPattern":"try { behavior.execute(execution); } catch (ActivitiException e) { if (e.getMessage().contains(\"anything else than a boundary event\")) { log.error(\"Behavior miswired for element {}\", execution.getCurrentFlowElement(), e); } else { throw e; } }","preventionTips":["Only map BoundaryTimerEventActivityBehavior to BoundaryEvent+TimerEventDefinition in custom ActivityBehaviorFactory overrides.","Use IntermediateCatchEventActivityBehavior for standalone timer catch events.","Review custom parse handlers after engine upgrades, as default bindings may change."],"tags":["activiti","bpmn","timer","boundary-event","behavior-binding"],"backgroundTag":"internal-invariant-violation","analyzedSha":"56435b1a97deeafdc09dd40074b056c89fba5a8a","analyzedAt":"2026-09-09T21:00:06.703Z","contentChangedAt":"2026-09-09T21:00:06.703Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}