mastra-ai/mastra · error · Error

Invalid cron expression "${cron}": ${reason}

Error message

Invalid cron expression "${cron}": ${reason}

What it means

validateCron throws this when the cron string is non-empty but Croner rejects the pattern itself (e.g. wrong field count, out-of-range values). The original Croner message is included as the reason so the specific syntax problem is visible.

Source

Thrown at packages/core/src/workflows/scheduler/cron.ts:23

 *
 * @param cron - Cron expression (5-, 6-, or 7-part).
 * @param timezone - Optional IANA timezone (e.g. 'America/New_York').
 */
export function validateCron(cron: string, timezone?: string): void {
  if (typeof cron !== 'string' || cron.trim() === '') {
    throw new Error(
      `Invalid cron expression: expected a non-empty cron string (e.g. "0 * * * *"), but received ${cron === undefined ? 'undefined' : JSON.stringify(cron)}.`,
    );
  }
  // Croner throws synchronously on an invalid pattern when the job is
  // constructed. Validate the pattern on its own first so timezone problems
  // (which croner only surfaces lazily) are not mislabeled as cron errors.
  let job: Cron;
  try {
    job = new Cron(cron);
  } catch (error) {
    const reason = error instanceof Error ? error.message : String(error);
    throw new Error(`Invalid cron expression "${cron}": ${reason}`);
  }
  // The timezone is only exercised when a fire time is computed.
  if (timezone !== undefined) {
    try {
      new Cron(cron, { timezone }).nextRun();
    } catch (error) {
      const reason = error instanceof Error ? error.message : String(error);
      throw new Error(`Invalid timezone "${timezone}": ${reason}`);
    }
  } else {
    job.nextRun();
  }
}

/**
 * Compute the next fire time (ms since epoch) for a cron expression.
 *
 * @param cron - Cron expression.

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Read the embedded reason from Croner and fix the offending field(s) in the expression.
  2. Use a standard 5-part expression (minute hour day month weekday) or a supported 6/7-part form.
  3. Test the pattern with an online cron parser or by running computeNextFireAt to confirm it yields a time.
  4. Keep cron strings in validated config/schema rather than free-form strings.

Example fix

// before
validateCron('0 25 * * *'); // hour 25 is invalid
// after
validateCron('0 5 * * *');
Defensive patterns

Strategy: validation

Validate before calling

function preCheckCron(cron: string) {
  const parts = cron.trim().split(/\s+/);
  if (parts.length < 5 || parts.length > 7) throw new Error(`cron must have 5-7 fields, got ${parts.length}: "${cron}"`);
}

Try / catch

try {
  validateCron(cron);
} catch (e) {
  const msg = (e as Error).message;
  if (msg.startsWith('Invalid cron expression "')) {
    console.error(`Bad cron pattern: ${cron}. ${msg}`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validateCron('99 99 * * *') (out-of-range fields), validateCron('* * *') (too few parts), or any malformed pattern such as '*/foo * * * *' — any input where `new Cron(cron)` throws synchronously.

Common situations: Typos in cron fields; using seconds-level 6/7-part syntax incorrectly; copying cron from another system with unsupported syntax; hand-editing schedules in production.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/e06a791232e4f11c. Report an issue: GitHub.