jackwener/OpenCLI · error · ArgumentError

${label} must be YYYY-MM-DD, got ${JSON.stringify(value)}

Error message

${label} must be YYYY-MM-DD, got ${JSON.stringify(value)}

What it means

normalizeDate validates that a date parameter matches the strict YYYY-MM-DD format before building the search URL. It throws ArgumentError when the value is non-empty but does not satisfy /^\d{4}-\d{2}-\d{2}$/. This rejects formats like MM/DD/YYYY, ISO timestamps, or relative dates so downstream URL construction only ever sees canonical dates.

Source

Thrown at clis/booking/search.js:40

function normalizeNonNegativeInt(value, defaultValue, label, max) {
  const raw = value ?? defaultValue;
  const n = Number(raw);
  if (!Number.isInteger(n) || n < 0) {
    throw new ArgumentError(`${label} must be a non-negative integer`);
  }
  if (typeof max === 'number' && n > max) {
    throw new ArgumentError(`${label} must be <= ${max}`);
  }
  return n;
}

function normalizeDate(value, label) {
  const v = String(value || '').trim();
  if (!v) {
    throw new ArgumentError(`${label} is required (YYYY-MM-DD)`);
  }
  if (!DATE_RE.test(v)) {
    throw new ArgumentError(`${label} must be YYYY-MM-DD, got ${JSON.stringify(value)}`);
  }
  const [year, month, day] = v.split('-').map(Number);
  const d = new Date(Date.UTC(year, month - 1, day));
  if (
    Number.isNaN(d.getTime()) ||
    d.getUTCFullYear() !== year ||
    d.getUTCMonth() !== month - 1 ||
    d.getUTCDate() !== day
  ) {
    throw new ArgumentError(`${label} is not a valid calendar date: ${v}`);
  }
  return v;
}

function normalizeCurrency(value) {
  if (value == null || value === '') return '';
  const v = String(value).trim().toUpperCase();
  if (!/^[A-Z]{3}$/.test(v)) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Reformat the value to strict YYYY-MM-DD (zero-padded month and day) before calling, e.g. with new Date(...).toISOString().slice(0, 10)
  2. Strip any time component: take the first 10 characters of an ISO string
  3. Convert Date objects via toISOString().slice(0,10) rather than template-string interpolation
  4. Verify the locale of the source picker and reorder DD/MM/YYYY parts into YYYY-MM-DD

Example fix

// before
const checkin = new Date();
await search(page, { checkin, checkout: '2026-09-01' });
// after
const checkin = new Date().toISOString().slice(0, 10);
await search(page, { checkin, checkout: '2026-09-01' });
Defensive patterns

Strategy: validation

Validate before calling

function isValidDateString(v) {
  return typeof v === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(v);
}
if (!isValidDateString(checkin) || !isValidDateString(checkout)) {
  throw new Error('checkin/checkout must be YYYY-MM-DD strings');
}

Type guard

function isYYYYMMDD(v) {
  return typeof v === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(v);
}

Try / catch

try {
  await search(page, { destination, checkin, checkout });
} catch (e) {
  if (/must be YYYY-MM-DD/.test(e.message)) {
    checkin = new Date(checkin).toISOString().slice(0, 10);
    checkout = new Date(checkout).toISOString().slice(0, 10);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the search command with checkin or checkout values like '08/28/2026', '2026-8-28', '2026-08-28T00:00:00Z', 'today+7', or a JS Date object stringified to a non-ISO form; any truthy value whose String() form fails DATE_RE.

Common situations: Passing a Date object instead of a formatted string; using locale-formatted dates from a UI picker (e.g. DD/MM/YYYY); supplying unix timestamps or 'now'/'tomorrow' shortcuts; copying dates with trailing time components from API responses.

Related errors


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