jackwener/OpenCLI · error · ArgumentError

currency must be a 3-letter ISO code (e.g. USD, JPY, CNY), g

Error message

currency must be a 3-letter ISO code (e.g. USD, JPY, CNY), got ${JSON.stringify(value)}

What it means

normalizeCurrency validates the optional currency parameter as a 3-letter A-Z code (uppercased and trimmed first). It throws ArgumentError when a non-empty currency value is not exactly three letters after normalization. It does not validate ISO-4217 membership — only the shape — so '' or null are allowed (meaning default) but 'US' or 'USDD' are rejected.

Source

Thrown at clis/booking/search.js:59

  }
  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)) {
    throw new ArgumentError(`currency must be a 3-letter ISO code (e.g. USD, JPY, CNY), got ${JSON.stringify(value)}`);
  }
  return v;
}

const ALLOWED_LANGS = new Set([
  'en-us', 'en-gb', 'zh-cn', 'zh-tw', 'ja', 'ko', 'de', 'fr', 'es', 'it',
  'pt-br', 'pt-pt', 'ru', 'th', 'vi', 'tr', 'pl', 'nl', 'ar',
]);

function normalizeLang(value) {
  if (value == null || value === '') return '';
  const v = String(value).trim().toLowerCase();
  if (!ALLOWED_LANGS.has(v)) {
    throw new ArgumentError(`lang must be one of: ${[...ALLOWED_LANGS].join(', ')}`);
  }
  return v;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Change the value to the 3-letter ISO 4217 code, e.g. 'USD', 'JPY', 'CNY'
  2. Trim surrounding whitespace and remove symbols from the config/env value
  3. Validate shape with /^[A-Za-z]{3}$/ before calling
  4. Check the source system is not emitting numeric ISO codes and map 840 -> USD if so

Example fix

// before
const currency = '$';
await search(page, { destination: 'Tokyo', currency });
// after
const currency = 'USD';
await search(page, { destination: 'Tokyo', currency });
Defensive patterns

Strategy: type-guard

Validate before calling

function normalizeCurrencySafe(v) {
  if (v == null || v === '') return '';
  const s = String(v).trim().toUpperCase();
  if (!/^[A-Z]{3}$/.test(s)) throw new Error(`bad currency: ${v}`);
  return s;
}

Type guard

function isCurrencyCode(v) {
  return typeof v === 'string' && /^[A-Za-z]{3}$/.test(v.trim());
}

Try / catch

try {
  await search(page, { destination, currency });
} catch (e) {
  if (/3-letter ISO code/.test(e.message)) {
    throw new Error(`Set currency to a 3-letter ISO code like USD; got: ${currency}`);
  } else throw e;
}

Prevention

When it happens

Trigger: Passing currency values like 'US', 'us-dollar', '$', 'USDollar', or numbers to the search command; a config value that contains a currency symbol or full currency name instead of the code.

Common situations: Using '$' or '€' from UI input; storing 'US Dollar' in a settings file; concatenating a numeric code (840) instead of the alphabetic code; trailing whitespace/full-width characters in config.

Related errors


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