{"record":{"id":"7bcfbc80aa3b5531","repo":"jackwener/OpenCLI","slug":"unknown-city-cityarg-pass-a-numeric-cityid-o","errorCode":null,"errorMessage":"unknown city '${cityArg}'. pass a numeric cityId or one of: ${names}","messagePattern":"unknown city '(.+?)'\\. pass a numeric cityId or one of: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/dianping/utils.js","lineNumber":59,"sourceCode":"    xiamen: 15, '厦门': 15,\n    hefei: 110, '合肥': 110,\n};\n\nexport const SEARCH_COLUMNS = ['rank', 'shop_id', 'name', 'rating', 'reviews', 'price', 'cuisine', 'district', 'url'];\nexport const SHOP_COLUMNS = ['field', 'value'];\n\n/**\n * Resolve a city argument (name or id) to a numeric cityId.\n * Returns null when the cookie's default city should be used.\n */\nexport function resolveCityId(cityArg) {\n    if (cityArg == null || cityArg === '') return null;\n    const raw = String(cityArg).trim().toLowerCase();\n    if (/^\\d+$/.test(raw)) return Number(raw);\n    const id = CITY_ID[raw];\n    if (!id) {\n        const names = Object.keys(CITY_ID).filter((k) => /^[a-z]+$/.test(k)).join(', ');\n        throw new ArgumentError(\n            'city',\n            `unknown city '${cityArg}'. pass a numeric cityId or one of: ${names}`,\n        );\n    }\n    return id;\n}\n\nexport function requireSearchLimit(value) {\n    const raw = value == null || value === '' ? 15 : value;\n    const limit = typeof raw === 'number' ? raw : Number(String(raw).trim());\n    if (!Number.isInteger(limit) || limit < 1 || limit > 15) {\n        throw new ArgumentError('limit must be an integer between 1 and 15 (dianping single page)');\n    }\n    return limit;\n}\n\nexport function normalizeShopId(rawInput) {\n    const raw = String(rawInput || '').trim();","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/dianping/utils.js#L41-L77","documentation":"resolveCityId resolves the --city argument to a numeric dianping cityId by accepting a digit string or a key of the CITY_ID map (English or Chinese names). If the argument is non-numeric and not a known city key, the library throws this ArgumentError listing all accepted English city names. A null/empty argument is allowed and means 'use the cookie's default city', so this error only fires for a genuinely unrecognized name.","triggerScenarios":"Passing a city name not in the CITY_ID map (e.g. 'newyork', 'suzhou ' typos aside, 'guangdong', '东莞市', pinyin like 'shenzhenshi'), a misspelled name such as 'xian ' vs 'xian' works but 'xi\\u2019an' or 'sian' does not, or passing a Chinese name variant that differs from the map keys.","commonSituations":"Typo in city name; passing a province or district instead of a supported city; passing a city dianping supports but this small built-in map omits (the map only has 20 common cities); locale/input-method issues producing full-width characters.","solutions":["Use one of the listed English names exactly (beijing, shanghai, guangzhou, shenzhen, hangzhou, chengdu, chongqing, nanjing, suzhou, xian, wuhan, tianjin, qingdao, changsha, dalian, shenyang, kunming, fuzhou, xiamen, hefei).","Look up the numeric cityId on dianping.com (/citylist or the /search/keyword/{cityId}/... URL) and pass the number instead — any digit string is accepted.","Check for typos, extra whitespace, or full-width characters; the lookup lowercases and trims but does not fuzzy-match or accept pinyin variants.","Extend the CITY_ID map (or open an issue/PR) if your city is supported by dianping but missing from the built-in list."],"exampleFix":"// before\nresolveCityId('shenzen'); // ArgumentError: unknown city\n// after\nresolveCityId('shenzhen'); // 7\n// or pass the numeric id directly\nresolveCityId('7'); // 7","handlingStrategy":"validation","validationCode":"const KNOWN = ['beijing','shanghai','guangzhou','shenzhen','hangzhou','chengdu','chongqing','nanjing','suzhou','xian','wuhan','tianjin','qingdao','changsha','dalian','shenyang','kunming','fuzhou','xiamen','hefei'];\nfunction isValidCityArg(v) {\n  if (v == null || v === '') return true; // default city\n  const raw = String(v).trim().toLowerCase();\n  return /^\\d+$/.test(raw) || KNOWN.includes(raw);\n}","typeGuard":"function isCityArg(v) {\n  return typeof v === 'string' && (/^\\d+$/.test(v.trim()) || CITY_ID.hasOwnProperty(v.trim().toLowerCase()));\n}","tryCatchPattern":"try {\n  const cityId = resolveCityId(cityArg);\n} catch (e) {\n  if (e.name === 'ArgumentError' && String(e.message).startsWith('unknown city')) {\n    console.error(`City '${cityArg}' not in built-in map; pass a numeric cityId from dianping /citylist`);\n  } else throw e;\n}","preventionTips":["Always pass numeric cityIds in scripts — names depend on the built-in 20-city map.","Copy cityIds from the dianping /search/keyword/{cityId}/... URL structure.","Normalize inputs: trim, lowercase, avoid full-width characters from CJK input methods.","Omit the city argument entirely to use the cookie's default city."],"tags":["validation","argument-error","city-lookup"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}