{"record":{"id":"b9539f70c90b5b66","repo":"jackwener/OpenCLI","slug":"eastmoney-convertible-returned-invalid-json-err","errorCode":null,"errorMessage":"eastmoney convertible returned invalid JSON: ${error?.message ?? error}","messagePattern":"eastmoney convertible returned invalid JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/eastmoney/convertible.js","lineNumber":156,"sourceCode":"    const url = new URL('https://push2.eastmoney.com/api/qt/clist/get');\n    url.searchParams.set('pn', '1');\n    url.searchParams.set('pz', String(limit));\n    url.searchParams.set('po', sort.order === 'desc' ? '1' : '0');\n    url.searchParams.set('np', '1');\n    url.searchParams.set('fltt', '2');\n    url.searchParams.set('invt', '2');\n    url.searchParams.set('fid', sort.fid);\n    url.searchParams.set('fs', 'b:MK0354');\n    url.searchParams.set('fields', 'f12,f14,f2,f3,f6,f229,f230,f232,f234,f235,f236,f237,f238,f239,f243');\n    url.searchParams.set('ut', 'bd1d9ddb04089700cf9c27f6f7426281');\n\n    const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\n    if (!resp.ok) throw new CommandExecutionError(`eastmoney convertible failed: HTTP ${resp.status}`);\n    let data;\n    try {\n      data = await resp.json();\n    } catch (error) {\n      throw new CommandExecutionError(`eastmoney convertible returned invalid JSON: ${error?.message ?? error}`);\n    }\n    const diff = extractConvertibleDiff(data);\n\n    return mapConvertibleRows(diff, limit);\n  },\n});\n\nexport const __test__ = { SORTS, extractConvertibleDiff, mapConvertibleRow, mapConvertibleRows, parseConvertibleLimit };\n","sourceCodeStart":138,"sourceCodeEnd":165,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/eastmoney/convertible.js#L138-L165","documentation":"CommandExecutionError thrown (clis/eastmoney/convertible.js:156) when `resp.json()` rejects — i.e. the HTTP response was OK (2xx) but the body is not valid JSON. The original parse error message is embedded. Eastmoney endpoints normally return JSON, so this indicates the body was HTML or otherwise non-JSON despite a success status.","triggerScenarios":"The push2 endpoint returns HTTP 200 with an HTML anti-bot/verification page, a captive-portal or proxy interception page, truncated/garbled transfer, or a `charset` mismatch that breaks JSON.parse. Also fires if a middlebox rewrites the response body.","commonSituations":"WAF soft-blocks that still return 200; hotel/office captive portals intercepting HTTPS through a misconfigured proxy; eastmoney serving a region-block interstitial; network middleboxes stripping content-encoding and corrupting the body.","solutions":["Log the raw response text to see whether it's HTML (block page) or truncated JSON.","Retry — transient interception often clears on a second request.","Check proxy/VPN/captive-portal interference and try a direct connection.","Send a fuller browser User-Agent to avoid WAF interstitials.","If persistent, inspect whether the endpoint moved and accept a content-type check before parsing."],"exampleFix":"// before\nconst data = await resp.json();\n// after\nconst text = await resp.text();\nlet data;\ntry {\n  data = JSON.parse(text);\n} catch (e) {\n  throw new Error('non-JSON body: ' + text.slice(0, 200));\n}","handlingStrategy":"try-catch","validationCode":"const ct = resp.headers.get('content-type') || '';\nif (!ct.includes('json')) throw new Error(`expected JSON, got ${ct}; likely blocked page`);\nconst text = await resp.text();","typeGuard":null,"tryCatchPattern":"try {\n  data = await resp.json();\n} catch (error) {\n  const body = await resp.text().catch(() => '');\n  console.error('non-JSON body preview:', body.slice(0, 200));\n  if (/html/i.test(body)) console.error('blocked/interstitial page — change IP or User-Agent');\n  return retryWithBackoff(fetchConvertible);\n}","preventionTips":["Check content-type before calling resp.json().","Capture and log the raw text on parse failure for diagnosis.","Avoid routes through captive portals or rewriting proxies when scraping.","Retry once on parse failure — interception is often transient.","Use a realistic browser User-Agent to avoid HTML interstitials."],"tags":["network","json","eastmoney","anti-bot"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}