{"record":{"id":"5ec8386090fb64d4","repo":"jackwener/OpenCLI","slug":"name-must-be-a-3-letter-iata-code-got-json","errorCode":null,"errorMessage":"--${name} must be a 3-letter IATA code, got ${JSON.stringify(raw)}","messagePattern":"--(.+?) must be a 3-letter IATA code, got (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/utils.js","lineNumber":241,"sourceCode":"    // Cross-check via UTC date math so 2026-02-30 doesn't pass.\n    const parsed = new Date(Date.UTC(year, month - 1, day));\n    if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== day) {\n        throw new ArgumentError(`--${name} is not a real calendar date: ${value}`);\n    }\n    return value;\n}\n\n/**\n * Validate a 3-letter IATA airport / metro code, return uppercase.\n * Ctrip URL accepts both single-airport (PEK / PVG) and metro-group (BJS / SHA) codes.\n */\nexport function parseIataCode(name, raw) {\n    if (raw === undefined || raw === null || raw === '') {\n        throw new ArgumentError(`--${name} is required (3-letter IATA code, e.g. PEK, SHA)`);\n    }\n    const value = String(raw).trim().toUpperCase();\n    if (!/^[A-Z]{3}$/.test(value)) {\n        throw new ArgumentError(`--${name} must be a 3-letter IATA code, got ${JSON.stringify(raw)}`);\n    }\n    return value;\n}\n\n/**\n * Validate a numeric Ctrip city ID (returned by `ctrip search` / `ctrip hotel-suggest`).\n */\nexport function parseCityId(raw) {\n    if (raw === undefined || raw === null || raw === '' || String(raw).trim() === '') {\n        throw new ArgumentError('--city is required (numeric city ID from `ctrip search` or `ctrip hotel-suggest`)');\n    }\n    try {\n        return parseStrictPositiveInteger('city', raw);\n    } catch {\n        throw new ArgumentError(`--city must be a positive integer city ID, got ${JSON.stringify(raw)}`);\n    }\n}\n","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/utils.js#L223-L259","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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}.","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."],"exampleFix":"// before\nctrip flights --from \"Beijing\" --to sha\n// after\nctrip flights --from PEK --to SHA","handlingStrategy":"validation","validationCode":"function validIata(v) { return typeof v === 'string' && /^[A-Za-z]{3}$/.test(v.trim()); }\nif (!validIata(from) || !validIata(to)) throw new Error('from/to must be 3-letter IATA codes');","typeGuard":"const isIataCode = (v) => typeof v === 'string' && /^[A-Z]{3}$/.test(v.trim().toUpperCase());","tryCatchPattern":"try {\n  const code = parseIataCode('from', raw);\n} catch (e) {\n  if (e instanceof ArgumentError) { console.error('Usage: --from PEK (3-letter IATA code)'); process.exitCode = 2; }\n  else throw e;\n}","preventionTips":["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."],"tags":["cli","validation","argument-error","iata-code"],"backgroundTag":"invalid-cli-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}