{"record":{"id":"a047a7908e51470f","repo":"jackwener/OpenCLI","slug":"dianping-contexthint","errorCode":null,"errorMessage":"dianping ${contextHint}","messagePattern":"dianping (.+?)","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"info","filePath":"clis/dianping/utils.js","lineNumber":119,"sourceCode":" * redirects to verify.meituan.com (Yoda icon-tap captcha) or to a login\n * page when the cookie is missing.\n */\nexport function detectAuthOrPageFailure({ text = '', url = '' }, contextHint, { emptyPatterns = [] } = {}) {\n    const signal = `${url} ${text}`;\n    if (/verify\\.meituan\\.com|verifyimg|身份核实|请依次点击|美团安全验证|Yoda/i.test(signal)) {\n        throw new AuthRequiredError(\n            'dianping.com',\n            `dianping ${contextHint} blocked by captcha — open ${url || 'www.dianping.com'} manually in this profile and solve the captcha, then retry`,\n        );\n    }\n    if (/login\\.dianping\\.com|account\\.dianping\\.com|请先登录|未登录|请登录/.test(signal)) {\n        throw new AuthRequiredError(\n            'dianping.com',\n            `dianping ${contextHint} requires login — sign in to dianping.com in this profile, then retry`,\n        );\n    }\n    if (emptyPatterns.some((pattern) => pattern.test(signal))) {\n        throw new EmptyResultError(`dianping ${contextHint}`);\n    }\n    const sample = text ? `; sample: ${String(text).slice(0, 160)}` : '';\n    throw new CommandExecutionError(\n        `dianping ${contextHint} did not render expected data${sample}`,\n        'This usually means dianping changed its HTML, returned an unexpected error page, or the browser profile hit an unrecognized anti-bot state.',\n    );\n}\n\n/**\n * Parse \"21231\" / \"1.2万\" review-count strings into integers.\n * Returns null when the input has no parseable digits.\n */\nexport function parseReviewCount(raw) {\n    if (raw == null) return null;\n    const s = String(raw).trim();\n    if (!s) return null;\n    const wanMatch = s.match(/^([\\d.]+)\\s*万/);\n    if (wanMatch) {","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/dianping/utils.js#L101-L137","documentation":"The final fallback of detectAuthOrPageFailure: when the page matched neither captcha nor login patterns and none of the caller-supplied emptyPatterns matched, it throws EmptyResultError('dianping <contextHint>') if an empty pattern did match, and otherwise CommandExecutionError 'did not render expected data'. EmptyResultError here means the page genuinely rendered but contained no matching data — dianping returned an empty result set for the query.","triggerScenarios":"A dianping search/detail step whose rendered HTML matches one of the emptyPatterns regexes passed by the adapter — e.g. searching a keyword with zero shops in the chosen city, a category filter with no matches, or a deleted/closed shop page.","commonSituations":"Overly narrow search keywords, mismatched cityId vs keyword (shops only exist in another city), filters (price/cuisine/district) that exclude all results, or a shop that has closed since the id was collected.","solutions":["Broaden the search keyword or remove filters and retry.","Check the cityId: resolveCityId maps names to ids; try the shop's actual city or omit it to use the cookie's default city.","Verify the shop id still exists by opening www.dianping.com/shop/<id> in a browser.","Treat it as an expected empty result if your query is legitimately unmatched — the CLI throws EmptyResultError so scripts can skip it (catch and continue)."],"exampleFix":"// before\nawait search({ city: 'xiamen', keyword: 'deep dish pizza' }); // EmptyResultError\n// after\ntry {\n  await search({ city: 'xiamen', keyword: 'pizza' });\n} catch (err) {\n  if (err.code === 'EMPTY_RESULT') return []; // expected: no matches\n  throw err;\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":"function isEmptyResultError(err) {\n  return err instanceof Error && err.code === 'EMPTY_RESULT';\n}","tryCatchPattern":"try {\n  results = await dianpingSearch(args);\n} catch (err) {\n  if (err.code === 'EMPTY_RESULT') {\n    results = []; // expected: no data for this query\n  } else throw err;\n}","preventionTips":["Sanity-check keyword/cityId combinations before batch runs (does the keyword have shops in that city?).","Catch EmptyResultError explicitly in batch scripts so one empty query doesn't abort the run.","Broaden filters progressively (drop price/cuisine/district constraints) when results come back empty.","Verify shop pages still exist before re-scraping saved ids — shops close."],"tags":["empty-result","search","dianping"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}