twentyhq/twenty · error · Error

Invalid cron expression: ${errorMessage}

Error message

Invalid cron expression: ${errorMessage}

What it means

Thrown by parseCronExpression after the cron-parser library rejects a cron string, or after an unexpected internal failure during field-splitting. The outer catch wraps any error (parse failure or the unreachable 'Unexpected error' fallback) and re-throws with the underlying message. This is the terminal validation gate for all cron-based workflow triggers in the Twenty frontend.

Source

Thrown at packages/twenty-front/src/modules/workflow/workflow-trigger/utils/cron-to-human/utils/parseCronExpression.ts:65

        dayOfWeek: parts[4],
      };
    } else if (parts.length === 6) {
      // Extended format: second minute hour day month dayOfWeek
      return {
        seconds: parts[0],
        minutes: parts[1],
        hours: parts[2],
        dayOfMonth: parts[3],
        month: parts[4],
        dayOfWeek: parts[5],
      };
    }

    throw new Error('Unexpected error in cron expression parsing');
  } catch (error) {
    const errorMessage =
      error instanceof Error ? error.message : 'Unknown error';
    throw new Error(`Invalid cron expression: ${errorMessage}`);
  }
};

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Validate the cron expression against cron-parser directly before calling parseCronExpression, using CronExpressionParser.parseTry() (non-throwing variant) to get a friendly preview.
  2. Ensure dayOfWeek uses numeric values 0-7 (0 and 7 both = Sunday) rather than names like 'MON'.
  3. Check each field range: minute 0-59, hour 0-23, dayOfMonth 1-31, month 1-12, dayOfWeek 0-7.
  4. If using step syntax '*/N', ensure N is a positive integer (not 0 or negative).

Example fix

// before
const parts = parseCronExpression(userInput);

// after
import { CronExpressionParser } from 'cron-parser';
try {
  CronExpressionParser.parse(userInput, { tz: 'UTC' });
} catch {
  throw new Error(`Please enter a valid cron expression. Use numeric fields (0-59 min, 0-23 hour, 1-31 day, 1-12 month, 0-7 weekday).`);
}
const parts = parseCronExpression(userInput);
Defensive patterns

Strategy: validation

Validate before calling

import { CronExpressionParser } from 'cron-parser';

const isParsableCron = (expr: string): boolean => {
  try {
    CronExpressionParser.parse(expr, { tz: 'UTC' });
    return true;
  } catch {
    return false;
  }
};

// Before calling parseCronExpression:
if (!isParsableCron(userInput)) {
  setFieldError('cron', 'Enter a valid cron expression (numeric fields only).');
  return;
}

Type guard

const isValidCronFieldRange = (field: string, min: number, max: number): boolean => {
  if (field === '*' || field.startsWith('*/')) return true;
  return field.split(',').every((part) => {
    const n = parseInt(part, 10);
    return !isNaN(n) && n >= min && n <= max;
  });
};

Try / catch

try {
  const parts = parseCronExpression(expression);
} catch (error) {
  if (error instanceof Error && error.message.startsWith('Invalid cron expression')) {
    // Show user-friendly validation message
    showCronValidationError(error.message);
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling parseCronExpression(expression) where expression has invalid field values (e.g. '99 99 * * *' — out-of-range minute/hour), unsupported syntax the normalizer cannot fix, or a 4-6 field string that cron-parser.rejects. Also fires if parts.length is 4-6 but none of the if/else-if branches match (defensive).

Common situations: User types a cron expression like '0 9 * * MONDAY' (named day not supported by this parser config), or '*/0 * * * *' (zero-step interval). Copy-pasting a cron string from a different system (e.g. AWS EventBridge with year field) that has 7 fields triggers the earlier field-count error, but a 5-field string with a bad range like '0 25 * * *' reaches this catch.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/88ac7f3f29ce08dd. Report an issue: GitHub.