{"record":{"id":"2ecafa86a7e0d012","repo":"apache/dolphinscheduler","slug":"cannot-parse-the-expression","errorCode":null,"errorMessage":"Cannot parse the expression: ","messagePattern":"Cannot parse the expression: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parser/TimePlaceholderUtils.java","lineNumber":273,"sourceCode":"            return true;\n        } else if (ADD_STRING.equals(peek) && (ADD_STRING.equals(cur) || SUBTRACT_STRING.equals(cur))) {\n            return true;\n        } else {\n            return SUBTRACT_STRING.equals(peek) && (ADD_STRING.equals(cur) || SUBTRACT_STRING.equals(cur));\n        }\n\n    }\n\n    /**\n     * Format time expression with given date, the date cannot be null.\n     * <p> If the expression is not a time expression, return the original expression.\n     *\n     */\n    public static String formatTimeExpression(final String timeExpression, final Date date,\n                                              final boolean ignoreInvalidExpression) {\n        // After N years: $[add_months(yyyyMMdd,12*N)], the first N months: $[add_months(yyyyMMdd,-N)], etc\n        if (date == null) {\n            throw new IllegalArgumentException(\"Cannot parse the expression: \" + timeExpression + \", date is null\");\n        }\n        if (StringUtils.isEmpty(timeExpression)) {\n            if (ignoreInvalidExpression) {\n                return timeExpression;\n            }\n            throw new IllegalArgumentException(\"Cannot format the date\" + date + \" with null timeExpression\");\n        }\n        try {\n            if (timeExpression.startsWith(TIMESTAMP)) {\n                return calculateTimeStamp(timeExpression, date);\n            }\n            if (timeExpression.startsWith(YEAR_WEEK)) {\n                return calculateYearWeek(timeExpression, date);\n            }\n            return calcTimeExpression(timeExpression, date);\n        } catch (Exception e) {\n            if (ignoreInvalidExpression) {\n                return timeExpression;","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/apache/dolphinscheduler/blob/02eac45a1b6676e639fcbfb4be2243de5771b05d/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parser/TimePlaceholderUtils.java#L255-L291","documentation":"TimePlaceholderUtils.formatTimeExpression formats a time expression like $[add_months(yyyyMMdd,12*N)] against a given Date. It throws IllegalArgumentException('Cannot parse the expression: <expr>, date is null') when the supplied Date is null, because no baseline date exists to shift or format. (An empty expression with ignoreInvalidExpression=false throws the sibling 'Cannot format the date' error.)","triggerScenarios":"Calling formatTimeExpression(timeExpression, null, false) — e.g. the scheduled/dependent fire time was not computed before placeholder substitution; a caller passes a null Date from a failed date calculation or from a parameter that was never set.","commonSituations":"Complement/dependent processing where the base schedule date is missing; calling the utility directly in custom plugins with an unset Date variable; a workflow triggered outside schedule context so the fire time is null.","solutions":["Ensure the baseline date is computed before substitution (e.g. derive from schedule fire time via DateUtils) and never pass null.","Guard the caller: if date == null, default to the workflow's scheduled time or now() depending on business semantics.","For dependent tasks, verify the upstream task's finish time exists — it feeds the date used here.","Catch IllegalArgumentException around formatTimeExpression only when ignoreInvalidExpression semantics are intended; otherwise let it fail loudly.","Check the expression string too: an empty expression triggers the related error; validate non-empty before calling with ignoreInvalidExpression=false."],"exampleFix":"// before\nString result = TimePlaceholderUtils.formatTimeExpression(expr, null, false);\n// after\nDate base = scheduleFireTime != null ? scheduleFireTime : new Date();\nString result = TimePlaceholderUtils.formatTimeExpression(expr, base, false);","handlingStrategy":"type-guard","validationCode":"if (date == null) { date = scheduleFireTime != null ? scheduleFireTime : new Date(); }\nif (timeExpression == null || timeExpression.isEmpty()) { throw new IllegalArgumentException(\"empty time expression\"); }","typeGuard":"boolean canFormat(String expr, Date d) { return d != null && expr != null && !expr.isEmpty(); }","tryCatchPattern":"try { out = TimePlaceholderUtils.formatTimeExpression(expr, baseDate, false); } catch (IllegalArgumentException e) { log.error(\"time expression failed: {}\", expr, e); throw new TaskException(\"time placeholder substitution failed\", e); }","preventionTips":["Always compute the schedule/fire date before placeholder substitution","Pass the upstream task finish time for dependent scheduling, defaulting to now when absent","Validate expressions non-empty before calling with ignoreInvalidExpression=false","Unit-test time expressions against fixed dates to catch null-date regressions"],"tags":["time-expression","date-parsing","placeholder"],"backgroundTag":"null-argument","analyzedSha":"02eac45a1b6676e639fcbfb4be2243de5771b05d","analyzedAt":"2026-09-06T17:43:00.555Z","contentChangedAt":"2026-09-06T17:43:00.555Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}