jackwener/OpenCLI · error · ArgumentError
--city must be a positive integer city ID, got ${JSON.string
Error message
--city must be a positive integer city ID, got ${JSON.stringify(raw)} What it means
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.
Source
Thrown at clis/ctrip/utils.js:256
}
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)}`);
}
}
/**
* Pick the best lat/lon from a Ctrip hotel `positionInfo.mapCoordinate` array.
*
* Each entry has a `coordinateType` (1=WGS84, 2=GCJ02, 3=BD09 / Baidu). We prefer
* WGS84 when present (most portable), then fall through. All coordinates are
* strings in the API, so we Number() and reject NaN.
*/
export function pickHotelMapCoords(mapCoordinate) {
if (!Array.isArray(mapCoordinate) || mapCoordinate.length === 0) {
return { lat: null, lon: null };
}
// Order: WGS84 (1) → GCJ02 (2) → BD09 (3) → whatever exists
const ranking = (entry) => {
const t = Number(entry?.coordinateType);
if (t === 1) return 0;View on GitHub (pinned to 49907e53dc)
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.
Example fix
// before ctrip hotels --city 12.5 // after ctrip hotels --city 2
Defensive patterns
Strategy: validation
Validate before calling
const n = Number(String(raw).trim());
if (!Number.isInteger(n) || n < 1) throw new Error('--city must be a positive integer, got ' + raw); Type guard
const isPositiveInt = (v) => typeof v === 'number' ? Number.isInteger(v) && v > 0 : /^\d+$/.test(String(v).trim());
Try / catch
try {
const cityId = parseCityId(raw);
} catch (e) {
if (e instanceof ArgumentError && e.message.includes('positive integer')) {
console.error('Copy the numeric ID exactly from `ctrip search` output.');
} else throw e;
} Prevention
- 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.
When it happens
Trigger: Passing --city with a non-integer or invalid number: 'abc', '1.5', '0', '-3', '12 ', '1e3', or values with attached units like 'city=2'.
Common situations: 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.
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
- hotel id must be a positive integer, got ${JSON.stringify(ra
- 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
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/51e7c863a001e5d5.
Report an issue: GitHub.