{"record":{"id":"83803c44014184d2","repo":"jackwener/OpenCLI","slug":"http-error-83803c","errorCode":"HTTP_ERROR","errorMessage":"eastmoney index-board failed: HTTP ${resp.status}","messagePattern":"eastmoney index-board failed: HTTP (.+?)","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/eastmoney/index-board.js","lineNumber":71,"sourceCode":"    /** @type {[string,string][]} */\n    let entries;\n    if (group === 'all') {\n      entries = [...INDEX_GROUPS.main, ...INDEX_GROUPS.hk, ...INDEX_GROUPS.us];\n    } else if (INDEX_GROUPS[group]) {\n      entries = INDEX_GROUPS[group];\n    } else {\n      throw new CliError('INVALID_ARGUMENT', `Unknown group \"${group}\". Valid: main, hk, us, all`);\n    }\n\n    const secids = entries.map(([secid]) => secid).join(',');\n    const url = new URL('https://push2.eastmoney.com/api/qt/ulist.np/get');\n    url.searchParams.set('secids', secids);\n    url.searchParams.set('fltt', '2');\n    url.searchParams.set('fields', FIELDS);\n    url.searchParams.set('ut', 'bd1d9ddb04089700cf9c27f6f7426281');\n\n    const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' } });\n    if (!resp.ok) throw new CliError('HTTP_ERROR', `eastmoney index-board failed: HTTP ${resp.status}`);\n    const data = await resp.json();\n    const diff = Array.isArray(data?.data?.diff) ? data.data.diff : [];\n    if (diff.length === 0) throw new CliError('NO_DATA', 'eastmoney returned no index data');\n\n    // Preserve the order defined in INDEX_GROUPS regardless of API ordering\n    const byCode = new Map(diff.map((it) => [String(it.f12), it]));\n    return entries\n      .map(([secid, fallbackName]) => {\n        const code = secid.split('.')[1];\n        const it = byCode.get(code);\n        if (!it) return null;\n        return {\n          code,\n          name: it.f14 || fallbackName,\n          price: it.f2,\n          changePercent: it.f3,\n          change: it.f4,\n          open: it.f17,","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/eastmoney/index-board.js#L53-L89","documentation":"This CliError with code HTTP_ERROR is thrown when the Eastmoney push2 ulist.np/get endpoint responds with a non-2xx HTTP status. The library checks resp.ok after fetching index quotes and surfaces the upstream status code in the message. It indicates a transport/response problem, not a data problem.","triggerScenarios":"Eastmoney returning 4xx/5xx for the ulist.np/get request: rate limiting or bot detection (429/403) due to missing/rejected User-Agent, temporary server errors (5xx), CDN/WAF blocking the client IP, or a malformed secids parameter causing a 400.","commonSituations":"Bulk-polling index boards and getting throttled by Eastmoney's anti-scraping layer; running from datacenter IPs that are blocked; Eastmoney API changes or outages; corporate proxy stripping the User-Agent header.","solutions":["Retry with exponential backoff, since 5xx and 429 are often transient","Slow down request frequency and add jitter to avoid rate limiting/bot detection","Verify the secids parameter is a well-formed comma list like 1.000001,0.399001 (a malformed list can trigger 400)","Test the exact URL in a browser or curl with the same User-Agent; if blocked, try a different network/IP","Check Eastmoney service status; if the endpoint changed, update the API path/params"],"exampleFix":"// before\nconst resp = await fetch(url); // one shot, throws HTTP_ERROR on 429/5xx\n// after\nfor (let i = 0; i < 3; i++) {\n  try { return await fetchBoard(); }\n  catch (e) { if (e.code === 'HTTP_ERROR' && i < 2) { await sleep(2 ** i * 500); continue; } throw e; }\n}","handlingStrategy":"retry","validationCode":"function validateSecids(entries) {\n  const list = entries.map((e) => String(e[0]));\n  if (list.some((s) => !/^[01]\\.\\d{6}$/.test(s))) {\n    throw new Error('Malformed secids; expected e.g. 1.000001,0.399001');\n  }\n  return list.join(',');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const board = await getIndexBoard({ group: 'main' });\n} catch (err) {\n  if (err.code === 'HTTP_ERROR') {\n    if (err.message.includes('HTTP 429') || /HTTP 5\\d\\d/.test(err.message)) {\n      return retryWithBackoff(() => getIndexBoard({ group: 'main' }), 3);\n    }\n    throw err; // non-retryable 4xx\n  }\n  throw err;\n}","preventionTips":["Throttle request rate and add jitter to avoid Eastmoney bot detection/rate limits","Implement exponential backoff for 429/5xx statuses","Send a browser-like User-Agent and Accept header as the library does","Test the exact request URL with curl when errors persist to distinguish blocking from outages"],"tags":["http","network","rate-limit","eastmoney"],"backgroundTag":"http-4xx-5xx-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}