{"record":{"id":"2854d830aec050e6","repo":"n8n-io/n8n","slug":"invalid-cron-expression-json-stringify-expressio","errorCode":null,"errorMessage":"Invalid cron expression ${JSON.stringify(expression)}: ${(error as Error).message}","messagePattern":"Invalid cron expression (.+?): (.+?)","errorType":"validation","errorClass":"InvalidScheduleError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/scheduler/src/core/recurrence/kinds/cron.ts","lineNumber":49,"sourceCode":"\t\t);\n\t}\n\n\tconst fieldCount = expression.trim().split(/\\s+/).length;\n\tif (fieldCount < MIN_CRON_FIELD_COUNT || fieldCount > MAX_CRON_FIELD_COUNT) {\n\t\tthrow new InvalidScheduleError(\n\t\t\t`Cron expression must have ${MIN_CRON_FIELD_COUNT} or ${MAX_CRON_FIELD_COUNT} fields (seconds optional), got ${fieldCount}: ${JSON.stringify(expression)}`,\n\t\t);\n\t}\n\n\t// A null timezone is the instance default, resolved by the caller.\n\tif (schedule.timezone !== null && !IANAZone.isValidZone(schedule.timezone)) {\n\t\tthrow new InvalidScheduleError(`Unknown IANA timezone: ${JSON.stringify(schedule.timezone)}`);\n\t}\n\n\ttry {\n\t\tCronExpressionParser.parse(expression, { tz: schedule.timezone ?? 'UTC' });\n\t} catch (error) {\n\t\tthrow new InvalidScheduleError(\n\t\t\t`Invalid cron expression ${JSON.stringify(expression)}: ${(error as Error).message}`,\n\t\t);\n\t}\n}\n\n/**\n * The schedule's timezone as a concrete IANA zone.\n * @param schedule The cron (or recurring_cron) schedule.\n * @returns The resolved timezone.\n * @throws {InvalidScheduleError} When the timezone is still `null` (the instance\n * default should have been resolved upstream).\n */\nexport function resolvedTimezone(schedule: CronSchedule | RecurringCronSchedule): string {\n\tif (schedule.timezone === null) {\n\t\tthrow new InvalidScheduleError(\n\t\t\t'Cron timezone must be resolved to a concrete zone before computing the next run, got null',\n\t\t);\n\t}","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/scheduler/src/core/recurrence/kinds/cron.ts#L31-L67","documentation":"Thrown by validateCron when CronExpressionParser.parse throws after the type, field-count, and timezone checks passed. The original parser error (e.g. 'Cannot use L/W modifiers with recurrent expressions') is wrapped with the offending expression so the user sees what was rejected.","triggerScenarios":"A 5/6-field expression that is syntactically invalid for the parser: out-of-range values ('60 0 * * *'), unsupported modifiers for the parser config ('0 0 * * 5L'), or duplicated/unparseable tokens.","commonSituations":"User writes a cron variant the underlying parser doesn't support (quartz-only modifiers in a unix cron parser); copy-paste from a different cron dialect; hand-built expressions with off-by-one ranges.","solutions":["Read the wrapped parser message - it identifies the bad field and value.","Use only standard unix cron syntax: 0-59 minutes, 0-23 hours, 1-31 dom, 1-12 month, 0-6 dow.","For 'last weekday of month' semantics, check the parser's supported modifier set before using L/W/#.","Pre-test expressions with a tool like crontab.guru before persisting."],"exampleFix":"// before\nvalidateCron({ kind: 'cron', cronExpression: '60 0 * * *', timezone: 'UTC' }); // minute 60 -> throws\n\n// after - clamp field into range\nvalidateCron({ kind: 'cron', cronExpression: '0 0 * * *', timezone: 'UTC' }); // every day at midnight","handlingStrategy":"validation","validationCode":"import { CronExpressionParser } from 'cron-parser';\n\nfunction preflightCron(expr: string): void {\n  try {\n    CronExpressionParser.parse(expr, { tz: 'UTC' });\n  } catch (e) {\n    throw new Error(`Cron preflight failed for ${JSON.stringify(expr)}: ${(e as Error).message}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  validateCron(schedule);\n} catch (e) {\n  if (e instanceof InvalidScheduleError) {\n    // Surface to the user with the field/value; do NOT retry with auto-correction.\n    return badRequest(`Invalid cron: ${e.message}`);\n  }\n  throw e;\n}","preventionTips":["Pre-test expressions with cron-parser in a UI playground before submission.","Restrict to standard unix cron ranges per field (0-59, 0-23, 1-31, 1-12, 0-6).","Document which modifiers (L, W, #) the parser supports before users reach for them.","Treat InvalidScheduleError as a user-input error, not a transient retry candidate."],"tags":["scheduler","cron","validation","syntax"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}