{"record":{"id":"c5e34072aecc2222","repo":"jackwener/OpenCLI","slug":"name-must-be-a-numeric-trip-com-hotel-id-got","errorCode":null,"errorMessage":"--${name} must be a numeric Trip.com hotel id, got ${JSON.stringify(raw)}","messagePattern":"--(.+?) must be a numeric Trip\\.com hotel id, got (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/trip/utils.js","lineNumber":323,"sourceCode":"    };\n    const found = detect();\n    if (found) return resolve(found);\n    const observer = new MutationObserver(() => {\n      const result = detect();\n      if (result) { observer.disconnect(); resolve(result); }\n    });\n    observer.observe(document.documentElement, { childList: true, subtree: true });\n    setTimeout(() => { observer.disconnect(); resolve('timeout'); }, 12000);\n  })\n`;\n\nexport function parseHotelId(name, raw) {\n    if (raw === undefined || raw === null || String(raw).trim() === '') {\n        throw new ArgumentError(`--${name} is required (numeric Trip.com hotel id, discover via the hotels list)`);\n    }\n    const value = String(raw).trim();\n    if (!/^\\d+$/.test(value)) {\n        throw new ArgumentError(`--${name} must be a numeric Trip.com hotel id, got ${JSON.stringify(raw)}`);\n    }\n    return value;\n}\n\nexport function buildHotelDetailUrl(hotelId) {\n    const params = new URLSearchParams({ hotelId, locale: 'en_US', curr: 'USD' });\n    return `https://www.trip.com/hotels/detail/?${params.toString()}`;\n}\n\n/**\n * Browser-context IIFE that projects the single-hotel profile from\n * `__NEXT_DATA__.props.pageProps.hotelDetailResponse` (the same SSR shape the\n * mainland `ctrip hotel` detail uses). Rating sub-scores, popular amenities, and\n * the check-in/out policy are each joined into one string so the profile stays a\n * single flat row. Returns `null` when the SSR block is absent, so the caller\n * raises a typed error instead of surfacing blanks. Room-level nightly prices\n * load via a post-SSR XHR and are out of scope here.\n */","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/trip/utils.js#L305-L341","documentation":"parseHotelId validates that a CLI option (e.g. --hotel) is a numeric Trip.com hotel id before building hotel detail URLs. When the supplied value contains anything other than digits after trimming (letters, punctuation, floats, negatives, whitespace inside), the function throws ArgumentError with the offending raw value JSON-stringified. Hotel ids on Trip.com are purely numeric strings, so any non-numeric input cannot resolve to a hotel.","triggerScenarios":"Calling any command whose CLI handler invokes hotelId/parseHotelId with a value like 'abc123', '12345.0', '-99', '12 34', or a URL slug such as 'Hotel/Detail?hotelId=123' passed by mistake instead of the bare id.","commonSituations":"Developers paste the full Trip.com hotel URL or slug instead of just the id; a config/env var holds a formatted or quoted value; shell quoting injects stray characters; automation scripts pass float-formatted ids from JSON sources.","solutions":["Find the numeric id via the hotels list command (discover endpoint) and pass only the digits, e.g. --hotel 1234567","Strip any non-numeric wrapping (URL, quotes, currency symbols) before passing the value: extract with a regex like /\\d+/ from a pasted URL","Check shell quoting/escaping so no stray characters are appended to the option value","Validate the value with /^\\d+$/ in your wrapper script before invoking the CLI"],"exampleFix":"// before\ntripcli hotel-detail --hotel 'https://www.trip.com/hotels/detail/?hotelId=1234567'\n// after\ntripcli hotel-detail --hotel 1234567","handlingStrategy":"validation","validationCode":"function isValidHotelId(v) { return typeof v === 'string' || typeof v === 'number' ? /^\\d+$/.test(String(v).trim()) : false; }\nif (!isValidHotelId(hotelId)) throw new Error(`hotel id must be numeric, got ${JSON.stringify(hotelId)}`);","typeGuard":"const isHotelId = (v) => (typeof v === 'string' || typeof v === 'number') && /^\\d+$/.test(String(v).trim());","tryCatchPattern":"try {\n  await runCli(['hotel-detail', '--hotel', hotelId]);\n} catch (e) {\n  if (/must be a numeric Trip.com hotel id/.test(e.message)) {\n    const digits = String(hotelId).match(/\\d+/);\n    if (digits) return runCli(['hotel-detail', '--hotel', digits[0]]);\n  }\n  throw e;\n}","preventionTips":["Always source hotel ids from the hotels list command, not from pasted URLs","Normalize inputs: extract digits with /\\d+/ before passing","Add a pre-call regex check (/^\\d+$/) in wrapper scripts","Avoid float/number formatting of ids from JSON — keep them as strings"],"tags":["cli","argument-validation","input-format"],"backgroundTag":"invalid-argument-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}