jackwener/OpenCLI · error · ArgumentError
--${name} must be a 3-letter IATA code, got ${JSON.stringify
Error message
--${name} must be a 3-letter IATA code, got ${JSON.stringify(raw)} What it means
parseIataCode validates CLI flags (e.g. --from/--to) that must be exactly three uppercase ASCII letters forming an IATA airport/city code. After trimming and uppercasing the raw input, anything not matching /^[A-Z]{3}$/ is rejected. It throws ArgumentError so the CLI fails fast with a clear message instead of sending a malformed code to Ctrip's sites.
Source
Thrown at clis/ctrip/utils.js:241
// 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;
}
/**
* Validate a 3-letter IATA airport / metro code, return uppercase.
* Ctrip URL accepts both single-airport (PEK / PVG) and metro-group (BJS / SHA) codes.
*/
export function parseIataCode(name, raw) {
if (raw === undefined || raw === null || raw === '') {
throw new ArgumentError(`--${name} is required (3-letter IATA code, e.g. PEK, SHA)`);
}
const value = String(raw).trim().toUpperCase();
if (!/^[A-Z]{3}$/.test(value)) {
throw new ArgumentError(`--${name} must be a 3-letter IATA code, got ${JSON.stringify(raw)}`);
}
return value;
}
/**
* Validate a numeric Ctrip city ID (returned by `ctrip search` / `ctrip hotel-suggest`).
*/
export function parseCityId(raw) {
if (raw === undefined || raw === null || raw === '' || String(raw).trim() === '') {
throw new ArgumentError('--city is required (numeric city ID from `ctrip search` or `ctrip hotel-suggest`)');
}
try {
return parseStrictPositiveInteger('city', raw);
} catch {
throw new ArgumentError(`--city must be a positive integer city ID, got ${JSON.stringify(raw)}`);
}
}
View on GitHub (pinned to 49907e53dc)
Solutions
- Look up the correct 3-letter IATA code (e.g. PEK, SHA, CAN) via `ctrip search` and pass it, upper- or lowercase (it is normalized).
- Remove surrounding characters/whitespace or digits from the value; only 3 letters are allowed.
- If you have an ICAO code (4 letters), convert it to the IATA code before calling.
- Ensure the flag is actually provided on the command line and not expanded to an empty shell variable.
Example fix
// before ctrip flights --from "Beijing" --to sha // after ctrip flights --from PEK --to SHA
Defensive patterns
Strategy: validation
Validate before calling
function validIata(v) { return typeof v === 'string' && /^[A-Za-z]{3}$/.test(v.trim()); }
if (!validIata(from) || !validIata(to)) throw new Error('from/to must be 3-letter IATA codes'); Type guard
const isIataCode = (v) => typeof v === 'string' && /^[A-Z]{3}$/.test(v.trim().toUpperCase()); Try / catch
try {
const code = parseIataCode('from', raw);
} catch (e) {
if (e instanceof ArgumentError) { console.error('Usage: --from PEK (3-letter IATA code)'); process.exitCode = 2; }
else throw e;
} Prevention
- Keep a codebook of IATA codes and resolve city names to codes via `ctrip search` before calling.
- Never pass ICAO (4-letter) codes; strip whitespace and quotes from shell arguments.
- Normalize with .trim().toUpperCase() and test /^[A-Z]{3}$/ in wrappers.
When it happens
Trigger: Calling a ctrip command (flight/train list via fromCode/toCode paths) with --from or --to set to undefined, an empty string, a 2- or 4-letter string, a code containing digits or non-Latin characters (e.g. 'PEK1', 'pek ', '北京'), or a full airport name like 'Beijing Capital'.
Common situations: Users pass city names instead of IATA codes, pass lowercase/double-quoted codes with stray whitespace that still contain extra characters, copy a 4-letter ICAO code (ZBAA) instead of the 3-letter IATA code (PEK), or forget the flag entirely on a shell where an empty variable expands to ''. Multi-byte Chinese input also fails since it isn't [A-Z]{3}.
Understand the failure class
Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.
Related errors
- archive search sort must be one of ${SORT_OPTIONS.join(', ')
- archive search mediatype must be one of ${MEDIATYPES.join(',
- archive search limit must be a positive integer
- archive search limit must be <= 100
- archive search query must not be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/5ec8386090fb64d4.
Report an issue: GitHub.