{"record":{"id":"95f3240e4bada4a0","repo":"n8n-io/n8n","slug":"recurring-cron-recurrencesize-must-be-an-integer-o","errorCode":null,"errorMessage":"recurring_cron.recurrenceSize must be an integer of at least 2 (a stride of 1 is a plain cron), got ${JSON.stringify(schedule.recurrenceSize)}","messagePattern":"recurring_cron\\.recurrenceSize must be an integer of at least 2 \\(a stride of 1 is a plain cron\\), got (.+?)","errorType":"validation","errorClass":"InvalidScheduleError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/scheduler/src/core/recurrence/kinds/recurring-cron.ts","lineNumber":35,"sourceCode":"\n/**\n * Checks an \"every N periods\" cron schedule: a valid cron expression, a known\n * period unit, and N of at least 2 (N = 1 keeps every fire, which is a plain\n * cron).\n * @param schedule The recurring_cron schedule to check.\n * @throws {InvalidScheduleError} When the cron, unit, or N is invalid.\n */\nexport function validateRecurringCron(schedule: RecurringCronSchedule): void {\n\tvalidateCron(schedule);\n\n\tif (!RecurringCronUnitList.includes(schedule.recurrenceUnit)) {\n\t\tthrow new InvalidScheduleError(\n\t\t\t`recurring_cron.recurrenceUnit must be one of ${RecurringCronUnitList.join(', ')}, got ${JSON.stringify(schedule.recurrenceUnit)}`,\n\t\t);\n\t}\n\n\tif (!Number.isInteger(schedule.recurrenceSize) || schedule.recurrenceSize < 2) {\n\t\tthrow new InvalidScheduleError(\n\t\t\t`recurring_cron.recurrenceSize must be an integer of at least 2 (a stride of 1 is a plain cron), got ${JSON.stringify(schedule.recurrenceSize)}`,\n\t\t);\n\t}\n}\n\n/**\n * Whether a candidate fire time should actually fire, for an \"every N periods\"\n * schedule. It only compares the candidate to the previous fire — there is no\n * hidden counter — so it gives the same answer on any machine and after a\n * restart.\n *\n * The candidate fires when either:\n * - it falls in the same period as the previous fire, so a rule like \"every 2\n *   weeks on Monday and Wednesday\" fires both days of a chosen week; or\n * - at least N periods have passed since the previous fire. \"At least\" (rather\n *   than \"exactly\") means that after downtime it resumes at the next fire\n *   instead of skipping a whole cycle.\n *","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/scheduler/src/core/recurrence/kinds/recurring-cron.ts#L17-L53","documentation":"Thrown by validateRecurringCron when recurrenceSize is not an integer or is less than 2. The recurring_cron kind implements 'every N periods' semantics; N=1 would keep every cron fire, which is identical to a plain cron schedule, so the minimum is enforced at 2 to keep the schedule kinds semantically distinct.","triggerScenarios":"A recurring_cron schedule is registered with recurrenceSize set to 1, 0, a negative number, a non-integer (e.g. 2.5), NaN, undefined, or null. ResolveRecurringCron reads the value from the job row and validateRecurringCron checks Number.isInteger(schedule.recurrenceSize) and schedule.recurrenceSize >= 2.","commonSituations":"Mistakenly setting recurrenceSize to 1 when the intent was a plain weekly cron (should use kind='cron' instead). Storing a float from user input without rounding. A migration or seed script that defaults recurrenceSize to 1 or leaves it unset. JSON deserialization turning a missing field into undefined.","solutions":["Set recurrenceSize to an integer >= 2 (e.g. 2 for 'every 2 weeks', 3 for 'every 3 months').","If you actually want every fire (N=1), switch the schedule kind to 'cron' instead of 'recurring_cron'.","Validate recurrenceSize is an integer >= 2 before persisting the job row.","Check the database row for the failing job and correct the recurrenceSize column."],"exampleFix":"// before\nconst schedule = {\n  kind: 'recurring_cron',\n  cronExpression: '0 9 * * 1',\n  recurrenceUnit: 'weeks',\n  recurrenceSize: 1, // stride of 1 is a plain cron\n};\n// after — for every-other-week, keep recurring_cron\nconst schedule = {\n  kind: 'recurring_cron',\n  cronExpression: '0 9 * * 1',\n  recurrenceUnit: 'weeks',\n  recurrenceSize: 2,\n};\n// OR use plain cron for every-week\n// const schedule = { kind: 'cron', cronExpression: '0 9 * * 1' };","handlingStrategy":"validation","validationCode":"function isValidRecurrenceSize(value: unknown): boolean {\n  return Number.isInteger(value) && (value as number) >= 2;\n}\n\nif (!isValidRecurrenceSize(schedule.recurrenceSize)) {\n  throw new Error('recurrenceSize must be an integer >= 2');\n}","typeGuard":"function isRecurrenceSize(value: unknown): value is number {\n  return typeof value === 'number' && Number.isInteger(value) && value >= 2;\n}","tryCatchPattern":"try {\n  validateRecurringCron(schedule);\n} catch (e) {\n  if (e instanceof InvalidScheduleError && e.message.includes('recurrenceSize')) {\n    // guide user to correct the value or switch to plain cron\n    throw new UserError('Set recurrenceSize to 2 or higher, or use a plain cron schedule');\n  }\n  throw e;\n}","preventionTips":["Validate recurrenceSize is an integer >= 2 at the form/API layer before saving.","If N=1 is desired, use kind='cron' instead of recurring_cron to avoid this error entirely.","Use a numeric input with min=2 in the UI to prevent invalid values."],"tags":["scheduler","validation","recurrence","configuration"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}