{"record":{"id":"30626a899f820b02","repo":"jackwener/OpenCLI","slug":"http-code","errorCode":null,"errorMessage":"HTTP ${code}","messagePattern":"HTTP \\$\\{code\\}","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ke/utils.js","lineNumber":94,"sourceCode":"    const result = await page.evaluate(`(async () => {\n    const res = await fetch(${JSON.stringify(url)}, { credentials: 'include' });\n    if (!res.ok) return { __keErr: res.status };\n    try {\n      return await res.json();\n    } catch {\n      return { __keErr: 'parse' };\n    }\n  })()`);\n    const r = result;\n    if (r?.__keErr !== undefined) {\n        const code = r.__keErr;\n        if (code === 401 || code === 403) {\n            throw new AuthRequiredError('ke.com', '未登录或登录已过期，请先在浏览器中登录贝壳找房');\n        }\n        if (code === 'parse') {\n            throw new CommandExecutionError('响应不是有效 JSON', '可能触发了风控，请检查登录状态或稍后重试');\n        }\n        throw new CommandExecutionError(`HTTP ${code}`, '请检查网络连接或登录状态');\n    }\n    return result;\n}\n\n/**\n * Build a ke.com city URL prefix. Default city is 'bj' (Beijing).\n */\nexport function cityUrl(city) {\n    return `https://${city}.ke.com`;\n}\n","sourceCodeStart":76,"sourceCodeEnd":105,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ke/utils.js#L76-L105","documentation":"fetchKeJson performs an in-browser fetch against ke.com APIs with credentials included. When the response is not ok (status other than 2xx) and it is not a 401/403 (handled by AuthRequiredError) and not a JSON-parse failure, it wraps the raw HTTP status into CommandExecutionError with message `HTTP ${code}`. This is the catch-all for non-auth HTTP failures like 404, 429, 5xx.","triggerScenarios":"Any ke.com JSON endpoint returns a non-2xx, non-401/403 status: e.g. 404 from a changed/renamed API path, 429 from rate limiting, 5xx from server errors, or 3xx followed by a non-OK terminal response.","commonSituations":"Ke.com rotates its internal API URLs so an old endpoint 404s; scraping too fast triggers 429 rate limiting; ke.com serves an HTML error page (5xx) during incidents; an invalid city prefix builds a bad URL.","solutions":["Read the HTTP code in the message: 429 means slow down / retry later; 404/400 means the API URL may have changed — update it; 5xx means retry after ke.com recovers","Check network connectivity and that the browser session can reach ke.com at all","Re-login in the browser to rule out session-related failures, then retry","If persistent, verify the URL passed to fetchKeJson (city prefix, query params) is still valid"],"exampleFix":"// before\nconst data = await fetchKeJson(page, oldEndpoint);\n// after\nlet data;\ntry {\n  data = await fetchKeJson(page, newEndpoint);\n} catch (e) {\n  if (/HTTP 429/.test(e.message)) { await sleep(5000); data = await fetchKeJson(page, newEndpoint); }\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"// health-check the endpoint before the real call\nconst probe = await page.evaluate(`fetch(${JSON.stringify(url)}, { credentials:'include', method:'HEAD' }).then(r => r.status).catch(() => 0)`);\nif (probe === 429) throw new Error('rate-limited, back off before calling fetchKeJson');","typeGuard":"function isKeResult(r) { return r != null && typeof r === 'object' && r.__keErr === undefined; }","tryCatchPattern":"try {\n  const data = await fetchKeJson(page, url);\n} catch (e) {\n  const m = /HTTP (\\d{3})/.exec(e.message);\n  if (m && Number(m[1]) === 429) { await sleep(5000); return fetchKeJson(page, url); }\n  throw e; // 404/5xx: surface to caller\n}","preventionTips":["Throttle requests to ke.com to avoid 429s","Keep API endpoint URLs in config so UI rotations can be patched quickly","Validate the city prefix/URL before fetching","Monitor ke.com availability and back off on 5xx"],"tags":["network","http","scraping","rate-limit"],"backgroundTag":"http-error-status","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}