{"record":{"id":"f130ca8fc85a5b6b","repo":"twentyhq/twenty","slug":"cron-expression-is-required","errorCode":null,"errorMessage":"Cron expression is required","messagePattern":"Cron expression is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/twenty-front/src/modules/workflow/workflow-trigger/utils/cron-to-human/describeCronExpression.ts","lineNumber":22,"sourceCode":"\nimport { getDayOfMonthDescription } from './descriptors/getDayOfMonthDescription';\nimport { getDayOfWeekDescription } from './descriptors/getDayOfWeekDescription';\nimport { getHoursDescription } from './descriptors/getHoursDescription';\nimport { getMinutesDescription } from './descriptors/getMinutesDescription';\nimport { getMonthsDescription } from './descriptors/getMonthsDescription';\nimport {\n  type CronDescriptionOptions,\n  DEFAULT_CRON_DESCRIPTION_OPTIONS,\n} from './types/cronDescriptionOptions';\nimport { parseCronExpression } from './utils/parseCronExpression';\n\nexport const describeCronExpression = (\n  expression: string,\n  options: CronDescriptionOptions = DEFAULT_CRON_DESCRIPTION_OPTIONS,\n  localeCatalog?: Locale,\n): string => {\n  if (!isDefined(expression) || expression.trim() === '') {\n    throw new Error('Cron expression is required');\n  }\n\n  try {\n    const parts = parseCronExpression(expression);\n    const mergedOptions = { ...DEFAULT_CRON_DESCRIPTION_OPTIONS, ...options };\n\n    const descriptions: string[] = [];\n\n    // Smart priority: Use hours description when we have specific hours,\n    // otherwise use minutes description for patterns like */5\n    const hoursDescription = getHoursDescription(\n      parts.hours,\n      parts.minutes,\n      mergedOptions,\n    );\n    const minutesDescription = getMinutesDescription(\n      parts.minutes,\n      mergedOptions,","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/cron-to-human/describeCronExpression.ts#L4-L40","documentation":"describeCronExpression guards its entry: it throws when `expression` is undefined/null or trims to empty, before any parsing attempt. This is input validation, distinct from the catch-all wrapper at line 114.","triggerScenarios":"Calling describeCronExpression('') , describeCronExpression(undefined as any), or with a whitespace-only string. UI rendering the cron human description before a schedule value has been set.","commonSituations":"Workflow trigger schedule UI rendered with an empty default. Programmatic call paths that pass through before the user picks a frequency.","solutions":["Check the value is a non-empty string before calling (early-return in the UI).","Provide a sensible default cron expression (e.g., '0 * * * *') for new workflow triggers.","Guard the call site: if (!expression) return '';"],"exampleFix":"// before: const desc = describeCronExpression(schedule ?? '');\n// after:  const desc = schedule ? describeCronExpression(schedule) : '';","handlingStrategy":"validation","validationCode":"const safeDescribeCron = (expr: string | undefined | null): string =>\n  expr && expr.trim() ? describeCronExpression(expr) : '';","typeGuard":"const isNonEmptyCronString = (v: unknown): v is string =>\n  typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":null,"preventionTips":["Default new workflow trigger schedules to a concrete cron (e.g., '0 * * * *') instead of empty.","Guard UI render of the human description with a truthy check on the schedule value."],"tags":["cron","workflow","validation"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}