{"record":{"id":"10fe80856a9a8864","repo":"twentyhq/twenty","slug":"cron-expression-is-required-10fe80","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/utils/parseCronExpression.ts","lineNumber":10,"sourceCode":"import { type CronExpressionParts } from '@/workflow/workflow-trigger/utils/cron-to-human/types/cronExpressionParts';\nimport { CronExpressionParser } from 'cron-parser';\nimport { isDefined } from 'twenty-shared/utils';\nimport { normalizeWhitespace } from './normalizeWhitespace';\n\nexport const parseCronExpression = (\n  expression: string,\n): CronExpressionParts => {\n  if (!isDefined(expression) || expression.trim() === '') {\n    throw new Error('Cron expression is required');\n  }\n\n  let normalized = normalizeWhitespace(expression);\n\n  normalized = normalized.replace(/(^|\\s)\\/(\\d+)/g, '$1*/$2');\n\n  const parts = normalized.split(/\\s+/);\n\n  if (parts.length < 4 || parts.length > 6) {\n    throw new Error(\n      `Invalid cron expression. Expected 4-6 fields, got ${parts.length}`,\n    );\n  }\n\n  try {\n    CronExpressionParser.parse(normalized, { tz: 'UTC' });\n\n    // Handle different cron formats that cron-parser accepts","sourceCodeStart":1,"sourceCodeEnd":28,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/cron-to-human/utils/parseCronExpression.ts#L1-L28","documentation":"Lower-level guard inside parseCronExpression: thrown when the input is undefined/null or trims to empty, before whitespace normalization and field splitting. This is the parse-layer counterpart to error 67.","triggerScenarios":"Direct or indirect call to parseCronExpression('') or parseCronExpression(undefined as any). describeCronExpression's guard (67) usually catches this first, so a direct hit means parseCronExpression was called on its own.","commonSituations":"Custom call sites that validate a cron string before describe. Test harness passing empty input.","solutions":["Validate non-empty at the caller before invoking parseCronExpression.","Return early / show empty state when the schedule field is blank."],"exampleFix":"// before: const parts = parseCronExpression(value); // value may be ''\n// after:  if (!value?.trim()) throw new Error('cron required');\n//         const parts = parseCronExpression(value);","handlingStrategy":"validation","validationCode":"const safeParseCron = (expr: string | undefined | null) =>\n  expr && expr.trim() ? parseCronExpression(expr) : null;","typeGuard":"const isNonEmptyCronString = (v: unknown): v is string =>\n  typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":null,"preventionTips":["Check the value is a non-empty string at the call site before parsing.","Prefer going through describeCronExpression which has its own guard and try/catch."],"tags":["cron","workflow","validation"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}