{"record":{"id":"b70ae1065ce14304","repo":"jackwener/OpenCLI","slug":"rawinput-does-not-look-like-a-guazi-clue-id","errorCode":null,"errorMessage":"'${rawInput}' does not look like a guazi clue id (a number, or a /car-detail/c<id>.html URL)","messagePattern":"'(.+?)' does not look like a guazi clue id \\(a number, or a /car-detail/c<id>\\.html URL\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/guazi/utils.js","lineNumber":75,"sourceCode":"\n/** Resolve a city arg (name or code) to a Guazi city code; defaults to bj. */\nexport function resolveCityCode(cityArg) {\n    if (cityArg == null || cityArg === '') return 'bj';\n    const raw = String(cityArg).trim().toLowerCase();\n    if (CITY_CODE[raw]) return CITY_CODE[raw];\n    if (CITY_CODE[String(cityArg).trim()]) return CITY_CODE[String(cityArg).trim()];\n    if (/^[a-z]{2,3}$/.test(raw)) return raw; // already a code\n    const names = Object.keys(CITY_CODE).filter((k) => /^[a-z]+$/.test(k)).join(', ');\n    throw new ArgumentError('city', `unknown city '${cityArg}'. pass a Guazi city code or one of: ${names}`);\n}\n\n/** Normalize a clue id: a bare number or a /car-detail/c<id>.htm(l) URL. */\nexport function normalizeClueId(rawInput) {\n    const raw = String(rawInput || '').trim();\n    if (!raw) throw new ArgumentError('clue_id must be a non-empty value');\n    const m = raw.match(/car-detail\\/c(\\d+)/) || raw.match(/^c?(\\d+)$/);\n    if (!m) {\n        throw new ArgumentError(`'${rawInput}' does not look like a guazi clue id (a number, or a /car-detail/c<id>.html URL)`);\n    }\n    return m[1];\n}\n\nexport function requireLimit(value, def, max) {\n    const raw = value == null || value === '' ? def : value;\n    const n = typeof raw === 'number' ? raw : Number(String(raw).trim());\n    if (!Number.isInteger(n) || n < 1 || n > max) {\n        throw new ArgumentError(`limit must be an integer between 1 and ${max}`);\n    }\n    return n;\n}\n\nexport function clean(s) {\n    return String(s == null ? '' : s).replace(/\\s+/g, ' ').trim();\n}\n\nexport function requireText(value, label) {","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/guazi/utils.js#L57-L93","documentation":"normalizeClueId extracts the numeric id via /car-detail\\/(\\d+)/ or ^c?(\\d+)$; if neither matches it throws this ArgumentError echoing the raw input. It fires when the value is non-empty but not shaped like a Guazi clue id.","triggerScenarios":"Passing 'car 100200300', 'https://m.guazi.com/car/100200300x.html' (non-digits in id), a full detail URL with a slug like '/car-detail/c100200300-bmw.html' (trailing slug breaks ^c?\\d+$ and the URL regex still matches though), or an alphanumeric SKU like 'abc123'.","commonSituations":"Copying a title line instead of the id from browse output; pasting a URL from a different site section (/buy/, /qiugou/); including surrounding text or quotes around the id in a script.","solutions":["Pass just the numeric id, e.g. 100200300, or the 'c'-prefixed form c100200300.","If passing a URL, make sure it contains /car-detail/c<digits> — strip query strings and extra path segments if needed.","Trim quotes/whitespace and remove any label text before invoking.","Extract the digits programmatically: (input.match(/(\\d+)/) || [])[1]."],"exampleFix":"// before\nguazi car --clue_id \"car 100200300\"\n// after\nguazi car --clue_id 100200300","handlingStrategy":"validation","validationCode":"function isClueId(v) {\n  const s = String(v ?? '').trim();\n  return /car-detail\\/c(\\d+)/.test(s) || /^c?\\d+$/.test(s);\n}\nif (!isClueId(input)) throw new Error(`not a clue id: ${input}`);","typeGuard":"function isClueId(v): v is string {\n  return typeof v === 'string' &&\n    (/car-detail\\/c\\d+/.test(v) || /^c?\\d+$/.test(v.trim()));\n}","tryCatchPattern":"try {\n  const car = await guaziCar({ clue_id: raw });\n} catch (e) {\n  if (/does not look like a guazi clue id/.test(e.message)) {\n    const m = String(raw).match(/(\\d+)/);\n    if (m) return guaziCar({ clue_id: m[1] });\n  }\n  throw e;\n}","preventionTips":["Extract just the digits before passing ids","Strip labels, quotes, and query strings from copied values","Validate with the same regexes the library uses","Keep ids in dedicated fields, not free-text lines"],"tags":["argument-validation","cli","id-format"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}