{"record":{"id":"948efb7fb857059d","repo":"jackwener/OpenCLI","slug":"json","errorCode":null,"errorMessage":"响应不是有效 JSON","messagePattern":"响应不是有效 JSON","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ke/utils.js","lineNumber":92,"sourceCode":" */\nexport async function fetchKeJson(page, url) {\n    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":74,"sourceCodeEnd":105,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ke/utils.js#L74-L105","documentation":"fetchKeJson got an HTTP 2xx response but res.json() failed, so the in-page fetch returns {__keErr:'parse'} and the helper throws CommandExecutionError('响应不是有效 JSON'). This usually means the endpoint returned HTML instead of JSON — most often a risk-control/captcha interstitial or a login page — so the hint points the user to check login state and retry later.","triggerScenarios":"Endpoint returns an HTML page (captcha, WAF block, login redirect) with status 200; a proxy/gateway injects an error page; the URL is wrong and hits the site's SPA shell; CDN returns compressed/mangled content the page parser can't decode.","commonSituations":"Beike风控 soft-blocks a session with a 200 HTML challenge; API path typo redirects to the website homepage; corporate proxy rewriting responses.","solutions":["Open the exact API URL in the logged-in browser and inspect the raw response","If it's a captcha/risk page, complete verification in the browser, wait, and retry","Re-login (ke auth login) to refresh the session","Verify the endpoint URL and query params are correct","Add request delays — repeated soft blocks indicate rate limiting"],"exampleFix":"// before\nconst r = await fetch(url, {credentials:'include'}); // 200 but HTML body -> __keErr:'parse'\n// after\nconst r = await fetch(url, {credentials:'include', headers:{'Accept':'application/json'}});\nconst ct = r.headers.get('content-type') || '';\nif (!ct.includes('json')) return { __keErr: 'parse' }; // fail fast with clearer signal","handlingStrategy":"validation","validationCode":"const r = await page.evaluate(`fetch(${JSON.stringify(url)}, {credentials:'include'}).then(async res => ({\n  status: res.status,\n  contentType: res.headers.get('content-type') || '',\n  head: (await res.text()).slice(0, 200)\n}))`);\nif (!r.contentType.includes('json')) {\n  console.log('Non-JSON response (' + r.contentType + '):', r.head);\n  console.log('Likely a captcha/WAF page — complete verification in the browser');\n}","typeGuard":"function looksLikeJson(html) {\n  const t = html.trim();\n  return t.startsWith('{') || t.startsWith('[');\n}","tryCatchPattern":"try {\n  const data = await fetchKeJson(page, apiUrl);\n} catch (e) {\n  if (/响应不是有效 JSON/.test(e.message)) {\n    console.error('Endpoint returned non-JSON (risk-control page?). ' +\n      'Verify login, solve any captcha, wait, and retry with lower frequency.');\n    await new Promise(r => setTimeout(r, 30_000));\n    return fetchKeJson(page, apiUrl); // single retry after cooldown\n  }\n  throw e;\n}","preventionTips":["Send Accept: application/json and validate content-type before parsing","Inspect the raw response body when a new endpoint starts failing","Lower request rate — soft blocks often return 200 + HTML challenge pages","Double-check endpoint URLs; typos redirect to the SPA shell (HTML, not JSON)"],"tags":["network","json","risk-control","ke"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}