{"record":{"id":"d16c50f8f6ca85a6","repo":"flowable/flowable-engine","slug":"error-while-firing-timer-border-event-activity","errorCode":null,"errorMessage":"Error while firing timer: border event activity ${nestedActivityId} not found","messagePattern":"Error while firing timer: border event activity (.+?) not found","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/jobexecutor/TimerExecuteNestedActivityJobHandler.java","lineNumber":53,"sourceCode":"\n    public static final String TYPE = \"timer-transition\";\n    public static final String PROPERTYNAME_TIMER_ACTIVITY_ID = \"activityId\";\n    public static final String PROPERTYNAME_END_DATE_EXPRESSION = \"timerEndDate\";\n\n    @Override\n    public String getType() {\n        return TYPE;\n    }\n\n    @Override\n    public void execute(Job job, String configuration, ExecutionEntity execution, CommandContext commandContext) {\n\n        String nestedActivityId = TimerEventHandler.getActivityIdFromConfiguration(configuration);\n\n        ActivityImpl borderEventActivity = execution.getProcessDefinition().findActivity(nestedActivityId);\n\n        if (borderEventActivity == null) {\n            throw new ActivitiException(\"Error while firing timer: border event activity \" + nestedActivityId + \" not found\");\n        }\n\n        try {\n            if (commandContext.getEventDispatcher().isEnabled()) {\n                commandContext.getEventDispatcher().dispatchEvent(\n                        ActivitiEventBuilder.createEntityEvent(FlowableEngineEventType.TIMER_FIRED, job),\n                        EngineConfigurationConstants.KEY_PROCESS_ENGINE_CONFIG);\n                dispatchActivityTimeoutIfNeeded(job, execution, commandContext);\n            }\n\n            borderEventActivity\n                    .getActivityBehavior()\n                    .execute(execution);\n        } catch (RuntimeException e) {\n            LOGGER.error(\"exception during timer execution\", e);\n            throw e;\n\n        } catch (Exception e) {","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/jobexecutor/TimerExecuteNestedActivityJobHandler.java#L35-L71","documentation":"Fired by TimerExecuteNestedActivityJobHandler when a persisted timer job fires but the boundary/timer event activity it references no longer exists in the deployed process definition. The job's handler configuration stores a nestedActivityId that is looked up via execution.getProcessDefinition().findActivity(); a null return means the job is stale relative to the current definition. The engine throws ActivitiException to abort job execution rather than silently firing into a non-existent activity.","triggerScenarios":"A boundary timer job (or intermediate timer catch event job) is persisted, then a new process definition version is deployed and old instances are migrated/removed, so the activityId in the job configuration no longer resolves in the process definition the execution points to.","commonSituations":"Redeploying an updated BPMN where the boundary event id was renamed or removed while timer jobs from the old version still exist; cascading deletion of process instances without deleting their timer jobs; manually manipulating ACT_RU_JOB rows; restoring a database backup out of sync with deployments.","solutions":["Delete the stale timer job (managementService.deleteJob(jobId)) or let the failed-job retry mechanism fail it (set exception message/stack trace on the job) so it stops blocking the async executor","Reconcile the deployment: redeploy the original BPMN version that contains the referenced activity, or migrate/restart the process instance so it references a definition containing the activityId","Check the job's JOB_HANDLER_CFG_ column for the nestedActivityId and verify it exists in the deployed BPMN XML (find the activity by id)","If whole version sets are stale, clean up orphaned jobs for dead process instances via SQL on ACT_RU_JOB where PROCESS_INSTANCE_ID no longer exists"],"exampleFix":"// before: failed job retrying forever\n// inspect the job and remove it\nJob job = managementService.createJobQuery().jobId(jobId).singleResult();\nmanagementService.deleteJob(job.getId());\n// after: ensure old-version jobs are removed when undeploying\nrepositoryService.deleteDeployment(deploymentId, true); // cascades instances and their jobs","handlingStrategy":"validation","validationCode":"Job job = managementService.createJobQuery().jobId(jobId).singleResult();\nString cfg = job.getJobHandlerConfiguration();\nString activityId = TimerEventHandler.getActivityIdFromConfiguration(cfg); // or parse JSON manually\nboolean exists = repositoryService.createProcessDefinitionQuery()\n    .processDefinitionId(job.getProcessDefinitionId()).singleResult() != null;\nif (!exists) { managementService.deleteJob(jobId); return; }","typeGuard":"boolean isStaleTimerJob(Job job) {\n  return job == null || job.getProcessDefinitionId() == null\n    || repositoryService.createProcessDefinitionQuery()\n        .processDefinitionId(job.getProcessDefinitionId()).singleResult() == null;\n}","tryCatchPattern":"try {\n  managementService.executeJob(jobId);\n} catch (FlowableException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"border event activity\")) {\n    managementService.deleteJob(jobId); // stale job, discard\n  } else { throw e; }\n}","preventionTips":["Always delete deployments with cascade=true when their instances are no longer needed, so timer jobs are removed too","Never rename/remove boundary timer activity ids without migrating or terminating running instances that have pending timers","Audit ACT_RU_JOB rows periodically for jobs whose PROCESS_DEFINITION_ID_ no longer exists in ACT_RE_PROCDEF","Run the same Flowable version on all cluster nodes to avoid job-configuration mismatches"],"tags":["job-executor","timer","process-definition","stale-job"],"backgroundTag":"resource-not-found","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"}