{"record":{"id":"28876f47e77f6901","repo":"twentyhq/twenty","slug":"invalid-cron-expression-expected-4-6-fields-got","errorCode":null,"errorMessage":"Invalid cron expression. Expected 4-6 fields, got ${parts.length}","messagePattern":"Invalid cron expression\\. Expected 4-6 fields, got (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/twenty-front/src/modules/workflow/workflow-trigger/utils/cron-to-human/utils/parseCronExpression.ts","lineNumber":20,"sourceCode":"import { 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\n    if (parts.length === 4) {\n      // Reduced format: hour day month dayOfWeek (minute defaults to 0)\n      return {\n        seconds: '0',\n        minutes: '0',\n        hours: parts[0],\n        dayOfMonth: parts[1],\n        month: parts[2],\n        dayOfWeek: parts[3],\n      };","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-front/src/modules/workflow/workflow-trigger/utils/cron-to-human/utils/parseCronExpression.ts#L2-L38","documentation":"Thrown by parseCronExpression after whitespace normalization and the `/N` -> `*/N` rewrite: the split must yield between 4 and 6 tokens. Fewer than 4 or more than 6 means the input is not a recognizable cron expression.","triggerScenarios":"Input like '*/5' (2 tokens), a full sentence, a cron with extra trailing fields, or stray spaces producing unexpected token counts. The `/N` rewrite only fixes a leading slash, not missing fields.","commonSituations":"User enters a partial cron ('*/5' instead of '*/5 * * * *'). Copy-paste with line breaks or tabs that normalizeWhitespace collapses oddly. Misconfigured preset that builds the cron string incorrectly.","solutions":["Count the space-separated fields of the input; pad to 5 with '*' for minute/hour/day/month/dayOfWeek as needed.","For '*/5' style inputs, append the missing fields: '*/5 * * * *'.","Validate token count in the UI before submission and show inline guidance."],"exampleFix":"// before: parseCronExpression('*/5')\n// after:  parseCronExpression('*/5 * * * *')","handlingStrategy":"validation","validationCode":"const countCronFields = (expr: string): number =>\n  expr.replace(/(^|\\s)\\/(\\d+)/g, '$1*/$2').trim().split(/\\s+/).filter(Boolean).length;\n// guard: const n = countCronFields(expr); if (n < 4 || n > 6) showError(`got ${n} fields`);","typeGuard":"const hasValidCronFieldCount = (expr: string): boolean => {\n  const n = countCronFields(expr);\n  return n >= 4 && n <= 6;\n};","tryCatchPattern":"try { parseCronExpression(expr); } catch (e) {\n  if (/Expected 4-6 fields/.test((e as Error).message)) setFieldError('Use 5 fields: minute hour day month dayOfWeek');\n}","preventionTips":["Provide a cron picker that always emits exactly 5 fields.","Auto-pad partial inputs ('*/5' -> '*/5 * * * *') before parsing.","Show inline field-count validation in the schedule editor."],"tags":["cron","workflow","validation"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}