{"record":{"id":"65c7a50ee406af28","repo":"flowable/flowable-engine","slug":"due-date-could-not-be-determined-for-timer-job-d","errorCode":null,"errorMessage":"Due date could not be determined for timer job ${dueDateString} for ${variableContainer}","messagePattern":"Due date could not be determined for timer job (.+?) for (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/util/TimerUtil.java","lineNumber":221,"sourceCode":"            timer.setJobHandlerType(jobHandlerType);\n            timer.setJobHandlerConfiguration(jobHandlerConfig);\n            timer.setExclusive(true);\n            timer.setRetries(processEngineConfiguration.getAsyncExecutorNumberOfRetries());\n            timer.setDuedate(duedate);\n\n            String jobCategoryElementText = resolveJobCategoryText(currentFlowElement);\n            if (jobCategoryElementText != null) {\n                Expression categoryExpression = processEngineConfiguration.getExpressionManager().createExpression(jobCategoryElementText);\n                Object categoryValue = categoryExpression.getValue(variableContainer);\n                if (categoryValue != null) {\n                    timer.setCategory(categoryValue.toString());\n                }\n            }\n\n        } else {\n            StringBuilder sb = new StringBuilder(\"Due date could not be determined for timer job \").append(dueDateString);\n            sb.append(\" for \").append(variableContainer);\n            throw new FlowableException(sb.toString());\n        }\n\n        if (StringUtils.isNotEmpty(timerEventDefinition.getTimeCycle())) {\n            // See ACT-1427: A boundary timer with a cancelActivity='true', doesn't need to repeat itself\n            boolean repeat = !isInterruptingTimer;\n\n            // ACT-1951: intermediate catching timer events shouldn't repeat according to spec\n            if (currentFlowElement instanceof IntermediateCatchEvent) {\n                repeat = false;\n            }\n\n            if (repeat) {\n                String prepared = prepareRepeat(dueDateString);\n                timer.setRepeat(prepared);\n            }\n        }\n\n","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/util/TimerUtil.java#L203-L239","documentation":"After parsing the timer's due date value and/or resolving it via the business calendar, the dueDate is still null. TimerUtil throws this FlowableException because a timer job cannot be created without a computable due date, even though some raw date string was present.","triggerScenarios":"timeDate/timeCycle/timeDuration resolve to a value (or dueDateString is produced) but the BusinessCalendar.resolveDuedate returns null or the value fails conversion so duedate remains null at the final check in createTimerEntity.","commonSituations":"Business calendar (e.g. 'businessCalendarName' ref) configured so the given date falls outside working hours and resolves to null; custom BusinessCalendar implementations returning null; malformed cycle expressions handled leniently upstream; custom calendars swapped in via config.","solutions":["Fix or replace the BusinessCalendar so resolveDuedate never returns null for valid input (return now/next-open instead).","Verify the dueDateString format matches what the selected business calendar can parse.","Check the calendarName reference on the timerEventDefinition points to a registered calendar in the BusinessCalendarManager.","Log/inspect dueDateString at runtime and unit test the calendar with the exact production values."],"exampleFix":"// before\nclass CustomCalendar implements BusinessCalendar {\n  public Date resolveDuedate(String d, ...) { return parseOrNull(d); }\n}\n// after\npublic Date resolveDuedate(String d, ...) {\n  Date parsed = parseOrNull(d);\n  return parsed != null ? parsed : new Date(); // never return null\n}","handlingStrategy":"validation","validationCode":"Date due = businessCalendar.resolveDuedate(dueDateString);\nif (due == null) throw new IllegalStateException(\"Business calendar returned null duedate for: \" + dueDateString);","typeGuard":null,"tryCatchPattern":"try { /* create timer */ } catch (FlowableException e) { if (e.getMessage().startsWith(\"Due date could not be determined\")) log.error(\"Calendar/timer misconfig: {}\", e.getMessage()); else throw e; }","preventionTips":["Unit-test custom BusinessCalendar implementations to never return null","Verify calendarName refs exist in the BusinessCalendarManager config","Validate due-date string formats against the active calendar"],"tags":["flowable","timer","business-calendar","invalid-date-format"],"backgroundTag":"invalid-date-format","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}