jackwener/OpenCLI · error · ArgumentError

--${name} has invalid month/day: ${value}

Error message

--${name} has invalid month/day: ${value}

What it means

After the regex matches, parseIsoDate range-checks the numeric month and day. This error is thrown when month is not 1-12 or day is not 1-31 — structurally plausible-looking digits that are still out of range. It fires before the full calendar check.

Source

Thrown at clis/trip/utils.js:41

        throw new ArgumentError(`--${name} must be a 3-letter IATA code, got ${JSON.stringify(raw)}`);
    }
    return value;
}

export function parseIsoDate(name, raw) {
    if (raw === undefined || raw === null || raw === '') {
        throw new ArgumentError(`--${name} is required (YYYY-MM-DD)`);
    }
    const value = String(raw).trim();
    const m = ISO_DATE_RE.exec(value);
    if (!m) {
        throw new ArgumentError(`--${name} must be YYYY-MM-DD, got ${JSON.stringify(raw)}`);
    }
    const year = Number(m[1]);
    const month = Number(m[2]);
    const day = Number(m[3]);
    if (month < 1 || month > 12 || day < 1 || day > 31) {
        throw new ArgumentError(`--${name} has invalid month/day: ${value}`);
    }
    // Cross-check via UTC date math so 2026-02-30 doesn't pass.
    const parsed = new Date(Date.UTC(year, month - 1, day));
    if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== day) {
        throw new ArgumentError(`--${name} is not a real calendar date: ${value}`);
    }
    return value;
}

export function parseListLimit(raw, fallback = 20) {
    if (raw === undefined || raw === null || raw === '') return fallback;
    const parsed = Number(raw);
    if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) {
        throw new ArgumentError(`--limit must be an integer between ${MIN_LIMIT} and ${MAX_LIMIT}, got ${JSON.stringify(raw)}`);
    }
    if (parsed < MIN_LIMIT || parsed > MAX_LIMIT) {
        throw new ArgumentError(`--limit must be between ${MIN_LIMIT} and ${MAX_LIMIT}, got ${parsed}`);
    }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Correct the month to 01-12 and the day to 01-31
  2. Check whether month and day were swapped (e.g. 2026-31-05 likely means 2026-05-31)
  3. Validate dates programmatically before passing them (e.g. with a date library or regex plus range check)
  4. If the source is data with ambiguous formats, normalize it to ISO YYYY-MM-DD first

Example fix

// before
clis-trip flights --depart 2026-13-01
// after
clis-trip flights --depart 2026-12-01
Defensive patterns

Strategy: validation

Validate before calling

function isPlausibleIsoDate(v) {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(v).trim());
  if (!m) return false;
  const month = Number(m[2]), day = Number(m[3]);
  return month >= 1 && month <= 12 && day >= 1 && day <= 31;
}
if (!isPlausibleIsoDate(depart)) throw new Error(`invalid month/day: ${depart}`);

Type guard

function hasValidMonthDay(v) {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(v).trim());
  return !!m && +m[2] >= 1 && +m[2] <= 12 && +m[3] >= 1 && +m[3] <= 31;
}

Try / catch

try {
  runTripCli(['flights', '--depart', depart]);
} catch (err) {
  if (err instanceof ArgumentError && /has invalid month\/day/.test(err.message)) {
    console.error(`Check month (01-12) and day (01-31) fields: ${err.message}`);
    process.exitCode = 2;
  } else throw err;
}

Prevention

When it happens

Trigger: Passing e.g. --depart 2026-13-01 (month 13), --date 2026-00-10 (month 0), --checkin 2026-05-32 (day 32), or --date 0000-01-00.

Common situations: Typos swapping month/day fields (e.g. 2026-31-05 meaning May 31); hand-typed dates; data imports with offset or malformed date columns; confusion between MM-DD and DD-MM orders.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/5c26ac8afab28909. Report an issue: GitHub.