{"record":{"id":"51e7c863a001e5d5","repo":"jackwener/OpenCLI","slug":"city-must-be-a-positive-integer-city-id-got-j","errorCode":null,"errorMessage":"--city must be a positive integer city ID, got ${JSON.stringify(raw)}","messagePattern":"--city must be a positive integer city ID, got (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/utils.js","lineNumber":256,"sourceCode":"    }\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\n/**\n * Pick the best lat/lon from a Ctrip hotel `positionInfo.mapCoordinate` array.\n *\n * Each entry has a `coordinateType` (1=WGS84, 2=GCJ02, 3=BD09 / Baidu). We prefer\n * WGS84 when present (most portable), then fall through. All coordinates are\n * strings in the API, so we Number() and reject NaN.\n */\nexport function pickHotelMapCoords(mapCoordinate) {\n    if (!Array.isArray(mapCoordinate) || mapCoordinate.length === 0) {\n        return { lat: null, lon: null };\n    }\n    // Order: WGS84 (1) → GCJ02 (2) → BD09 (3) → whatever exists\n    const ranking = (entry) => {\n        const t = Number(entry?.coordinateType);\n        if (t === 1) return 0;","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/utils.js#L238-L274","documentation":"After the presence check, parseCityId delegates to parseStrictPositiveInteger('city', raw); if that fails (non-numeric, float, negative, zero, or numeric with junk like '12abc'), it rethrows as ArgumentError '--city must be a positive integer city ID'. The value must be a strictly positive integer matching Ctrip's city ID scheme.","triggerScenarios":"Passing --city with a non-integer or invalid number: 'abc', '1.5', '0', '-3', '12 ', '1e3', or values with attached units like 'city=2'.","commonSituations":"Typo'd or hand-guessed city IDs, pasted IDs with trailing spaces or invisible characters, decimal/negative numbers, or mixing up hotel IDs with city IDs.","solutions":["Re-run `ctrip search` / `ctrip hotel-suggest` and copy the exact numeric city ID.","Trim the value and remove any non-digit characters before passing it.","Use a positive integer >= 1; zero, negatives, decimals and exponent notation are rejected."],"exampleFix":"// before\nctrip hotels --city 12.5\n// after\nctrip hotels --city 2","handlingStrategy":"validation","validationCode":"const n = Number(String(raw).trim());\nif (!Number.isInteger(n) || n < 1) throw new Error('--city must be a positive integer, got ' + raw);","typeGuard":"const isPositiveInt = (v) => typeof v === 'number' ? Number.isInteger(v) && v > 0 : /^\\d+$/.test(String(v).trim());","tryCatchPattern":"try {\n  const cityId = parseCityId(raw);\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('positive integer')) {\n    console.error('Copy the numeric ID exactly from `ctrip search` output.');\n  } else throw e;\n}","preventionTips":["Copy IDs programmatically from search output instead of typing them.","Trim and strip non-digits before passing values through scripts.","Remember city IDs and hotel IDs are different namespaces; do not interchange them."],"tags":["cli","validation","argument-error","integer-validation"],"backgroundTag":"invalid-cli-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}