{"record":{"id":"f62818b0dde60422","repo":"jackwener/OpenCLI","slug":"dianping-could-not-resolve-cityid-for-cityarg","errorCode":null,"errorMessage":"dianping could not resolve cityId for '${cityArg}' (pinyin=${pinyin}); the city page rendered without a /search/keyword/{id}/ link","messagePattern":"dianping could not resolve cityId for '(.+?)' \\(pinyin=(.+?)\\); the city page rendered without a /search/keyword/(.+?)/ link","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/dianping/cityResolver.js","lineNumber":93,"sourceCode":"            const known = 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, a pinyin slug (e.g. shantou), `\n                + `a Chinese name listed on dianping.com/citylist, or one of: ${known}`,\n            );\n        }\n    } else {\n        const known = 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, a pinyin slug (e.g. shantou), `\n            + `a Chinese name listed on dianping.com/citylist, or one of: ${known}`,\n        );\n    }\n\n    const cityId = await fetchCityIdByPinyin(page, pinyin);\n    if (!cityId) {\n        throw new CommandExecutionError(\n            `dianping could not resolve cityId for '${cityArg}' (pinyin=${pinyin}); `\n            + `the city page rendered without a /search/keyword/{id}/ link`,\n        );\n    }\n\n    RESOLVE_CACHE.set(lowered, cityId);\n    RESOLVE_CACHE.set(pinyin, cityId);\n    if (CHINESE_RE.test(raw)) RESOLVE_CACHE.set(raw, cityId);\n    return cityId;\n}\n\n/**\n * Read https://www.dianping.com/citylist and return a Chinese-name → pinyin\n * slug map for every city link present on the page. Used when the user\n * supplied a Chinese name that isn't in the static map.\n */\nasync function lookupPinyinFromCitylist(page, chineseName) {\n    await page.goto('https://www.dianping.com/citylist');","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/dianping/cityResolver.js#L75-L111","documentation":"CommandExecutionError thrown after a valid pinyin slug was derived but fetchCityIdByPinyin could not extract a numeric cityId: the city page (www.dianping.com/<pinyin>/) rendered without any /search/keyword/{id}/ link to scrape the id from. The name was accepted; the runtime resolution against the live site failed.","triggerScenarios":"resolveCityIdAsync calls fetchCityIdByPinyin(page, pinyin); navigation to the city page succeeds but no anchor matching /search/keyword/\\d+/ appears — unknown pinyin slug with no Dianping city page, blocked/challenge page, or layout change.","commonSituations":"Made-up or misspelled pinyin slug with no corresponding Dianping city page; Dianping serving a captcha/verify page instead of the city page; Dianping markup change removing the /search/keyword/{id}/ link; regional redirect to a default city without that link.","solutions":["Verify the pinyin slug is a real Dianping city (open https://www.dianping.com/<slug>/ in a browser and check for the search link).","Use the numeric cityId directly to bypass online resolution, e.g. city=108.","Retry later or from a cleaner session — anti-bot/captcha pages can hide the search link.","If the site layout changed, update fetchCityIdByPinyin's selector to scrape the cityId from the new markup."],"exampleFix":"// before\nawait cli.dianping.search({ keyword: '火锅', city: 'shantou' }); // transient scrape failure\n// after\ntry { await cli.dianping.search({ keyword: '火锅', city: 'shantou' }); }\ncatch (e) { if (/could not resolve cityId/.test(e.message)) return cli.dianping.search({ keyword: '火锅', city: '108' }); throw e; }","handlingStrategy":"fallback","validationCode":"// check the city page resolves before the real call\nawait page.goto(`https://www.dianping.com/${slug}/`);\nconst ok = await page.evaluate(`!!document.querySelector('a[href*=\"/search/keyword/\"]')`);","typeGuard":null,"tryCatchPattern":"try { return await searchWithCity(slug); }\ncatch (e) { if (/could not resolve cityId/.test(e.message)) { return searchWithCity(FALLBACK_CITY_ID); } throw e; }","preventionTips":["Cache successfully resolved cityIds (the library's RESOLVE_CACHE does this per-run) and persist them.","Prefer hardcoded numeric cityIds for cities you query often.","Detect captcha/verify pages and back off instead of hammering resolution.","Verify slugs against dianping.com/citylist once and keep the mapping."],"tags":["scraping","dianping","city-resolution","network"],"backgroundTag":"city-id-resolution-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}