{"record":{"id":"aedcdba705b844f4","repo":"jackwener/OpenCLI","slug":"parse-error-aedcdb","errorCode":"PARSE_ERROR","errorMessage":"PARSE_ERROR: Invalid JSON response for ${path}","messagePattern":"PARSE_ERROR: Invalid JSON response for (.+?)","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/weread/utils.js","lineNumber":120,"sourceCode":" * Used by search and ranking commands (browser: false).\n */\nexport async function fetchWebApi(path, params) {\n    const url = new URL(`${WEB_API}${path}`);\n    if (params) {\n        for (const [k, v] of Object.entries(params))\n            url.searchParams.set(k, v);\n    }\n    const resp = await fetch(url.toString(), {\n        headers: { 'User-Agent': WEREAD_UA },\n    });\n    if (!resp.ok) {\n        throw new CliError('FETCH_ERROR', `HTTP ${resp.status} for ${path}`, 'WeRead API may be temporarily unavailable');\n    }\n    try {\n        return await resp.json();\n    }\n    catch {\n        throw new CliError('PARSE_ERROR', `Invalid JSON response for ${path}`, 'WeRead may have returned an HTML error page');\n    }\n}\n/**\n * Fetch a private WeRead API endpoint with cookies extracted from the browser.\n * The HTTP request itself runs in Node.js to avoid page-context CORS failures.\n *\n * Cookies are collected from both the API subdomain (i.weread.qq.com) and the\n * main domain (weread.qq.com). WeRead may set auth cookies as host-only on\n * weread.qq.com, which won't match i.weread.qq.com in a URL-based lookup.\n */\nexport async function fetchPrivateApi(page, path, params) {\n    const url = new URL(`${API}${path}`);\n    if (params) {\n        for (const [k, v] of Object.entries(params))\n            url.searchParams.set(k, v);\n    }\n    const urlStr = url.toString();\n    // Merge cookies from both domains; API-domain cookies take precedence on name collision","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread/utils.js#L102-L138","documentation":"Thrown by fetchWebApi when the response from a public WeRead web endpoint has HTTP success status but its body cannot be parsed as JSON (resp.json() rejects). The hint notes WeRead may have returned an HTML error page — meaning the request looked successful to HTTP semantics but the payload is not the expected JSON.","triggerScenarios":"resp.json() throws on the /web/* response body: an HTML captcha/WAF challenge page served with 200, an empty body, truncated/garbled response from a proxy, or Content-Type mismatch where the body is HTML text.","commonSituations":"Captive portals or corporate MITM proxies injecting HTML; WeRead anti-bot serving an HTML challenge with status 200; a CDN error page returned with 200; network middleware corrupting the response mid-transfer.","solutions":["Capture resp.text() and inspect the first bytes — if it's HTML (\"<!DOCTYPE\" or a captcha page), the request is being challenged; add cookies or browser-like headers.","Retry the request — a truncated transfer or transient proxy issue often resolves on retry.","Bypass suspect proxies/VPN and test direct connectivity with curl to compare bodies.","If a WAF challenge page is returned, route requests through a real browser session or a headless browser instead of raw fetch.","Catch CliError with code 'PARSE_ERROR' and fall back to an alternative data source (HTML scraping path)."],"exampleFix":"// before\ntry { return await resp.json(); } catch { throw new CliError('PARSE_ERROR', ...); }\n// after\nconst text = await resp.text();\ntry { return JSON.parse(text); }\ncatch { throw new CliError('PARSE_ERROR', `Invalid JSON for ${path}; body starts with: ${text.slice(0, 120)}`); }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const data = await callWereadPublicApi();\n} catch (e) {\n  if (e.code === 'PARSE_ERROR') {\n    // HTML error/captcha page likely; back off, or reroute through a browser session\n    await sleep(5000);\n    return retryOnceOrFallbackToScraping();\n  }\n  throw e;\n}","preventionTips":["Inspect resp.text() (first 200 chars) when JSON parsing fails to identify HTML challenges","Route requests through a real browser session if WAF challenges recur","Avoid VPN/proxy exit nodes likely flagged by Tencent's edge","Retry transient truncations; verify with curl if failures persist"],"tags":["json","parse-error","weread","html-response","anti-bot"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}