{"record":{"id":"e9dae409ed80720a","repo":"n8n-io/n8n","slug":"one-off-fireat-must-be-a-valid-date","errorCode":null,"errorMessage":"one_off.fireAt must be a valid Date","messagePattern":"one_off\\.fireAt must be a valid Date","errorType":"validation","errorClass":"InvalidScheduleError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/scheduler/src/core/recurrence/kinds/one-off.ts","lineNumber":13,"sourceCode":"import { InvalidScheduleError } from '../../errors';\nimport type { OneOffSchedule, ScheduledJob } from '../../types';\nimport { required } from '../field';\n\n/**\n * Checks that a one-off schedule has a valid fire time.\n * @param schedule The one-off schedule to check.\n * @throws {InvalidScheduleError} When `fireAt` is not a valid Date.\n */\nexport function validateOneOff(schedule: OneOffSchedule): void {\n\tconst fireAt: unknown = schedule.fireAt;\n\tif (!(fireAt instanceof Date) || Number.isNaN(fireAt.getTime())) {\n\t\tthrow new InvalidScheduleError('one_off.fireAt must be a valid Date');\n\t}\n}\n\n/**\n * The one-off fire time, or `null` if it has already passed.\n * @param schedule The one-off schedule.\n * @param after The instant to fire after.\n * @returns `fireAt` when it is still ahead of `after`, otherwise `null`.\n */\nexport function oneOffNextRun(schedule: OneOffSchedule, after: Date): Date | null {\n\treturn after.getTime() < schedule.fireAt.getTime() ? schedule.fireAt : null;\n}\n\n/**\n * The single fire of a one-off schedule: just `first`, then nothing.\n * @param first The one-off's fire time.\n * @returns A generator that yields `first` once.\n */","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/scheduler/src/core/recurrence/kinds/one-off.ts#L1-L31","documentation":"Thrown by validateOneOff when schedule.fireAt is not a Date instance or is an Invalid Date (getTime() returns NaN). Like the cron validator, oneOff guards `unknown` because raw DB rows can arrive untyped - a string timestamp or a number would otherwise produce a confusing downstream error.","triggerScenarios":"A one_off schedule loaded from a DB row where fire_at came back as a string (ORM not configured for Date), a number (epoch ms), null, or an Invalid Date from new Date('invalid'). Also a literal constructed with fireAt: '2025-01-01'.","commonSituations":"An ORM/driver that returns TIMESTAMP as ISO string unless explicitly mapped; JSON-deserialized input (JSON.parse yields string, not Date); new Date(undefined) producing Invalid Date; a test fixture that set fireAt: Date.now() (number) instead of new Date().","solutions":["Map the DB column to a Date in your ORM/driver config (TypeORM: { type: 'timestamp' } yields Date).","When deserializing from JSON, wrap: fireAt: new Date(raw.fireAt).","Validate with `value instanceof Date && !Number.isNaN(value.getTime())` before validateOneOff.","Reject rows whose fire_at cannot be parsed rather than passing them through."],"exampleFix":"// before\nvalidateOneOff({ kind: 'one_off', fireAt: row.fire_at }); // row.fire_at is a string -> throws\n\n// after - coerce at the boundary\nconst fireAt = new Date(row.fire_at as string);\nif (Number.isNaN(fireAt.getTime())) {\n  throw new Error(`row ${row.id} has unparseable fire_at`);\n}\nvalidateOneOff({ kind: 'one_off', fireAt });","handlingStrategy":"type-guard","validationCode":"function coerceFireAt(raw: unknown): Date {\n  const d = raw instanceof Date ? raw : new Date(raw as string);\n  if (!(d instanceof Date) || Number.isNaN(d.getTime())) {\n    throw new Error('fireAt must be a valid Date or ISO string');\n  }\n  return d;\n}\n\nvalidateOneOff({ kind: 'one_off', fireAt: coerceFireAt(row.fire_at) });","typeGuard":"function isValidFireAt(v: unknown): v is Date {\n  return v instanceof Date && !Number.isNaN(v.getTime());\n}","tryCatchPattern":null,"preventionTips":["Configure the ORM/driver to return TIMESTAMP columns as Date (TypeORM timestamp -> Date).","When deserializing JSON, wrap string timestamps with new Date(...) at the boundary.","Reject Invalid Date at the controller before it reaches the scheduler.","Never pass epoch numbers (Date.now()) where a Date is required - wrap them."],"tags":["scheduler","one-off","type-narrowing","date"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}