{"record":{"id":"572721ecded6865d","repo":"jackwener/OpenCLI","slug":"eastmoney-convertible-failed-http-resp-status","errorCode":null,"errorMessage":"eastmoney convertible failed: HTTP ${resp.status}","messagePattern":"eastmoney convertible failed: HTTP (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/eastmoney/convertible.js","lineNumber":151,"sourceCode":"    const sortKey = String(args.sort ?? 'turnover').toLowerCase();\n    const sort = SORTS[sortKey];\n    if (!sort) throw new ArgumentError(`Unknown sort \"${sortKey}\". Valid: ${Object.keys(SORTS).join(', ')}`);\n    const limit = parseConvertibleLimit(args.limit);\n\n    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":133,"sourceCodeEnd":165,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/eastmoney/convertible.js#L133-L165","documentation":"CommandExecutionError thrown after fetch (clis/eastmoney/convertible.js:151) when the HTTP response from push2.eastmoney.com/api/qt/clist/get has a non-2xx status (`!resp.ok`). The message embeds the status code. It wraps any server-side rejection — rate limiting, WAF block, token rejection, or upstream outage — into a single CLI-level failure.","triggerScenarios":"Any fetch to the clist endpoint returning 4xx/5xx: 403 from eastmoney's WAF for suspicious IPs or missing/rotated `ut` token, 429 from request-rate limits, 5xx from push2 outages, or proxy errors surfacing as non-OK statuses.","commonSituations":"Running from datacenter/cloud IPs commonly blocked by eastmoney; hammering the endpoint in a loop and tripping 429; eastmoney rotating the `ut` token causing 403; corporate proxies or firewalls rejecting the request; push2 maintenance windows returning 5xx.","solutions":["Read the status in the message: 403 → token/WAF issue, 429 → back off and slow the request rate, 5xx → retry later.","Retry with exponential backoff; transient 5xx/429 usually clear.","Send a realistic browser User-Agent (the CLI sends 'Mozilla/5.0'; a fuller UA string helps against WAF).","Check network path — disable VPN/proxy or try a residential IP if blocked.","Verify the endpoint is up by curling the exact URL and inspecting status/headers."],"exampleFix":"// before\nconst resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\nif (!resp.ok) throw new CommandExecutionError(`eastmoney convertible failed: HTTP ${resp.status}`);\n// after\nconst resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' } });\nif (resp.status === 429 || resp.status >= 500) {\n  await new Promise(r => setTimeout(r, 2000));\n  return fetchConvertible(); // retry once with backoff\n}\nif (!resp.ok) throw new CommandExecutionError(`eastmoney convertible failed: HTTP ${resp.status}`);","handlingStrategy":"retry","validationCode":"// Nothing to validate pre-call; validate status post-call:\nconst resp = await fetch(url);\nif (resp.status === 429 || resp.status >= 500) return retryWithBackoff();\nif (!resp.ok) throw new Error(`HTTP ${resp.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  const rows = await convertibleRows();\n} catch (e) {\n  const m = String(e.message).match(/HTTP (\\d+)/);\n  if (m) {\n    const status = Number(m[1]);\n    if (status === 429 || status >= 500) return retryWithBackoff(convertibleRows, 3);\n    if (status === 403) console.error('blocked: rotate IP/User-Agent or refresh ut token');\n  }\n  throw e;\n}","preventionTips":["Rate-limit requests to push2 (e.g. ≥1s spacing) to avoid 429.","Use a full browser User-Agent string to reduce WAF 403s.","Back off exponentially on 5xx/429 instead of immediate retries.","Monitor the ut token — replace it when 403s start appearing.","Prefer residential IPs over datacenter ranges for eastmoney endpoints."],"tags":["network","http","eastmoney","rate-limit"],"backgroundTag":"http-error-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}