nexu-io/open-design · error · Error

--schedule ${kind} time must be HH:MM (24h)

Error message

--schedule ${kind} time must be HH:MM (24h)

What it means

Thrown by parseScheduleFlag in the `od automation` CLI when the HH:MM portion of a `--schedule daily:HH:MM` or `--schedule weekdays:HH:MM` flag fails the 24h regex `/^[0-2]\d:[0-5]\d$/`. The hour and minute tokens are zero-padded with padStart before the test, so the check is purely on numeric range, not on whether you supplied leading zeros. The `${kind}` interpolates to `daily` or `weekdays`.

Source

Thrown at apps/daemon/src/cli.ts:10513

  }
  const parts = raw.split(':');
  const kind = parts[0];
  if (kind === 'hourly') {
    const minute = Number(parts[1]);
    if (!Number.isInteger(minute) || minute < 0 || minute > 59) {
      throw new Error('--schedule hourly requires :<minute>, 0-59');
    }
    return { kind: 'hourly', minute };
  }
  if (kind === 'daily' || kind === 'weekdays') {
    if (parts.length < 3) {
      throw new Error(`--schedule ${kind} requires :HH:MM[:TZ]`);
    }
    const hh = parts[1];
    const mm = parts[2];
    const time = `${hh.padStart(2, '0')}:${mm.padStart(2, '0')}`;
    if (!/^[0-2]\d:[0-5]\d$/.test(time)) {
      throw new Error(`--schedule ${kind} time must be HH:MM (24h)`);
    }
    const timezone = parts.slice(3).join(':') || 'UTC';
    return { kind, time, timezone };
  }
  if (kind === 'weekly') {
    if (parts.length < 4) {
      throw new Error('--schedule weekly requires :DAY:HH:MM[:TZ] (DAY is 0-6 or sun/mon/...)');
    }
    const dayToken = String(parts[1]).toLowerCase();
    let weekday;
    if (/^[0-6]$/.test(dayToken)) {
      weekday = Number(dayToken);
    } else if (AUTOMATION_WEEKDAY_TOKENS[dayToken] !== undefined) {
      weekday = AUTOMATION_WEEKDAY_TOKENS[dayToken];
    } else {
      throw new Error(`--schedule weekly day must be 0-6 or sun..sat (got "${parts[1]}")`);
    }
    const time = `${parts[2].padStart(2, '0')}:${parts[3].padStart(2, '0')}`;

View on GitHub (pinned to 5be4028344)

Solutions

  1. Use a plain 24h HH:MM value, e.g. `--schedule daily:14:30` (leading zeros optional, the parser pads).
  2. Remove any AM/PM suffix; 14:30 is 2:30pm, 09:30 is 9:30am.
  3. If you need a timezone, append it as a trailing colon segment: `--schedule daily:14:30:America/New_York`.
  4. Double-check the hour is 00-23 and the minute is 00-59 before submitting.

Example fix

// before
--schedule daily:2:30pm
// after
--schedule daily:14:30
Defensive patterns

Strategy: validation

Validate before calling

// Validate a daily/weekdays schedule time before invoking the CLI/parser.
function isValid24hTime(raw: string): boolean {
  const parts = raw.split(':');
  if (parts.length < 3) return false;
  const time = `${parts[1].padStart(2, '0')}:${parts[2].padStart(2, '0')}`;
  return /^[0-2]\d:[0-5]\d$/.test(time);
}
// usage: isValid24hTime('daily:14:30') === true

Try / catch

try {
  const schedule = parseScheduleFlag(raw);
} catch (err) {
  if (err instanceof Error && /time must be HH:MM/.test(err.message)) {
    // surface a user-facing 'enter a valid 24h time' message
  }
  throw err;
}

Prevention

When it happens

Trigger: Running `od automation create --schedule daily:25:30` (hour out of range), `--schedule weekdays:09:60` (minute >59), `--schedule daily:abc:def` (non-numeric), or `--schedule daily:2:30pm` (the `pm` suffix becomes the minute token and fails).

Common situations: Typing the time in 12-hour format with an AM/PM suffix, using `24:00` for midnight, appending seconds `09:30:00` (which actually passes here because only parts[1]/parts[2] are tested and the rest become timezone), or copy-pasting a locale-formatted time like `9.30`.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/04cec1d0b4568afc. Report an issue: GitHub.