n8n-io/n8n · error · InvalidScheduleError
recurring_cron.recurrenceSize must be an integer of at least
Error message
recurring_cron.recurrenceSize must be an integer of at least 2 (a stride of 1 is a plain cron), got ${JSON.stringify(schedule.recurrenceSize)} What it means
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.
Source
Thrown at packages/@n8n/scheduler/src/core/recurrence/kinds/recurring-cron.ts:35
/**
* Checks an "every N periods" cron schedule: a valid cron expression, a known
* period unit, and N of at least 2 (N = 1 keeps every fire, which is a plain
* cron).
* @param schedule The recurring_cron schedule to check.
* @throws {InvalidScheduleError} When the cron, unit, or N is invalid.
*/
export function validateRecurringCron(schedule: RecurringCronSchedule): void {
validateCron(schedule);
if (!RecurringCronUnitList.includes(schedule.recurrenceUnit)) {
throw new InvalidScheduleError(
`recurring_cron.recurrenceUnit must be one of ${RecurringCronUnitList.join(', ')}, got ${JSON.stringify(schedule.recurrenceUnit)}`,
);
}
if (!Number.isInteger(schedule.recurrenceSize) || schedule.recurrenceSize < 2) {
throw new InvalidScheduleError(
`recurring_cron.recurrenceSize must be an integer of at least 2 (a stride of 1 is a plain cron), got ${JSON.stringify(schedule.recurrenceSize)}`,
);
}
}
/**
* Whether a candidate fire time should actually fire, for an "every N periods"
* schedule. It only compares the candidate to the previous fire — there is no
* hidden counter — so it gives the same answer on any machine and after a
* restart.
*
* The candidate fires when either:
* - it falls in the same period as the previous fire, so a rule like "every 2
* weeks on Monday and Wednesday" fires both days of a chosen week; or
* - at least N periods have passed since the previous fire. "At least" (rather
* than "exactly") means that after downtime it resumes at the next fire
* instead of skipping a whole cycle.
*View on GitHub (pinned to 5ac6606e81)
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.
Example fix
// before
const schedule = {
kind: 'recurring_cron',
cronExpression: '0 9 * * 1',
recurrenceUnit: 'weeks',
recurrenceSize: 1, // stride of 1 is a plain cron
};
// after — for every-other-week, keep recurring_cron
const schedule = {
kind: 'recurring_cron',
cronExpression: '0 9 * * 1',
recurrenceUnit: 'weeks',
recurrenceSize: 2,
};
// OR use plain cron for every-week
// const schedule = { kind: 'cron', cronExpression: '0 9 * * 1' }; Defensive patterns
Strategy: validation
Validate before calling
function isValidRecurrenceSize(value: unknown): boolean {
return Number.isInteger(value) && (value as number) >= 2;
}
if (!isValidRecurrenceSize(schedule.recurrenceSize)) {
throw new Error('recurrenceSize must be an integer >= 2');
} Type guard
function isRecurrenceSize(value: unknown): value is number {
return typeof value === 'number' && Number.isInteger(value) && value >= 2;
} Try / catch
try {
validateRecurringCron(schedule);
} catch (e) {
if (e instanceof InvalidScheduleError && e.message.includes('recurrenceSize')) {
// guide user to correct the value or switch to plain cron
throw new UserError('Set recurrenceSize to 2 or higher, or use a plain cron schedule');
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- recurring_cron.recurrenceUnit must be one of ${RecurringCron
- jitterRatio must be at least 0 and below 1, got ${lifecycleO
- ${key} must be a positive number of seconds, got ${value}
- concurrencyMode must be 'sequential' or 'concurrent', got ${
- maxConcurrentPasses must be a positive integer, got ${maxCon
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/95f3240e4bada4a0.
Report an issue: GitHub.