{"record":{"id":"ef605df5d5c56481","repo":"jackwener/OpenCLI","slug":"http-error-ef605d","errorCode":"HTTP_ERROR","errorMessage":"`longhu failed: HTTP ${resp.status}`","messagePattern":"`longhu failed: HTTP (.+?)`","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/eastmoney/longhu.js","lineNumber":46,"sourceCode":"  ],\n  columns: ['tradeDate', 'code', 'name', 'closePrice', 'changeRate', 'boardAmt', 'buyAmt', 'sellAmt', 'netAmt', 'turnover', 'dealRatio', 'market', 'reason'],\n  func: async (args) => {\n    const sinceDate = String(args.date || '').trim() || defaultTradeDate();\n    const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));\n\n    const url = new URL('https://datacenter-web.eastmoney.com/api/data/v1/get');\n    url.searchParams.set('sortColumns', 'TRADE_DATE,SECURITY_CODE');\n    url.searchParams.set('sortTypes', '-1,1');\n    url.searchParams.set('pageSize', String(limit));\n    url.searchParams.set('pageNumber', '1');\n    url.searchParams.set('reportName', 'RPT_DAILYBILLBOARD_DETAILS');\n    url.searchParams.set('columns', 'ALL');\n    url.searchParams.set('source', 'WEB');\n    url.searchParams.set('client', 'WEB');\n    url.searchParams.set('filter', `(TRADE_DATE>='${sinceDate}')`);\n\n    const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\n    if (!resp.ok) throw new CliError('HTTP_ERROR', `longhu failed: HTTP ${resp.status}`);\n    const data = await resp.json();\n    /** @type {any[]} */\n    const rows = Array.isArray(data?.result?.data) ? data.result.data : [];\n    if (rows.length === 0) throw new CliError('NO_DATA', `No longhu data since ${sinceDate}`);\n\n    return rows.slice(0, limit).map((it) => ({\n      tradeDate: String(it.TRADE_DATE || '').slice(0, 10),\n      code: it.SECURITY_CODE,\n      name: it.SECURITY_NAME_ABBR,\n      closePrice: it.CLOSE_PRICE,\n      changeRate: it.CHANGE_RATE,\n      boardAmt: it.BILLBOARD_DEAL_AMT,\n      buyAmt: it.BILLBOARD_BUY_AMT,\n      sellAmt: it.BILLBOARD_SELL_AMT,\n      netAmt: it.BILLBOARD_NET_AMT,\n      turnover: it.ACCUM_AMOUNT,\n      dealRatio: it.DEAL_AMOUNT_RATIO,\n      market: it.TRADE_MARKET,","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/eastmoney/longhu.js#L28-L64","documentation":"The eastmoney longhu CLI throws CliError('HTTP_ERROR') when the push2.eastmoney.com API responds with a non-OK status (resp.ok is false). It aborts before attempting resp.json() because the body is not trusted to be valid JSON. The thrown message embeds the HTTP status code so the caller can tell whether it is a rate limit (429), bad request (400), or server error (5xx).","triggerScenarios":"Any fetch to the eastmoney longhu (Dragon-Tiger list) endpoint whose response status is outside 200-299: eastmoney WAF/rate-limit pages returning 403/429, endpoint schema changes returning 400 for the filter `(TRADE_DATE>='${sinceDate}')`, or transient 5xx outages of push2.eastmoney.com.","commonSituations":"Hitting the public eastmoney API too frequently from scripts or CI (rate limiting), an invalid/malformed sinceDate making the filter expression rejected, corporate proxies blocking the request, or eastmoney deprecating/changing the WEB endpoint so the server rejects the parameter combination.","solutions":["Check the HTTP status in the message: 403/429 means rate-limited — slow down requests and add delays/backoff between calls.","Verify the sinceDate argument is a valid 'YYYY-MM-DD' string; a malformed date can make the filter expression fail server-side.","Retry after a few minutes if the status is 5xx — eastmoney infrastructure is often transiently unavailable.","Curl the same URL with the same User-Agent header outside the CLI to confirm whether the endpoint itself is reachable.","Check for eastmoney API changes (columns/source/client/fs params) if the failure is persistent across all dates."],"exampleFix":"// before\nconst resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\nif (!resp.ok) throw new CliError('HTTP_ERROR', `longhu failed: HTTP ${resp.status}`);\n// after — retry with backoff before giving up\nlet resp;\nfor (let attempt = 0; attempt < 3; attempt++) {\n  resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\n  if (resp.ok) break;\n  if (resp.status < 500 && resp.status !== 429) break; // non-retryable\n  await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));\n}\nif (!resp.ok) throw new CliError('HTTP_ERROR', `longhu failed: HTTP ${resp.status}`);","handlingStrategy":"try-catch","validationCode":"// Non-blocking pre-check: verify the endpoint is reachable and the CLI is not rate-limited\nconst res = await fetch('https://push2.eastmoney.com', { headers: { 'User-Agent': 'Mozilla/5.0' } });\nif (res.status === 403 || res.status === 429) console.warn('eastmoney is throttling this client; expect HTTP_ERROR');","typeGuard":"function isCliError(e) { return e instanceof Error && 'code' in e; }\nfunction isHttpError(e) { return isCliError(e) && e.code === 'HTTP_ERROR'; }","tryCatchPattern":"try {\n  const rows = await getLonghu({ since, limit });\n} catch (err) {\n  if (err instanceof CliError && err.code === 'HTTP_ERROR') {\n    if (/HTTP (429|403)/.test(err.message)) {\n      await sleep(5000); return getLonghu({ since, limit }); // backoff and retry once\n    }\n    console.error(`eastmoney unreachable (${err.message}); try again later`);\n    process.exitCode = 69; // EX_UNAVAILABLE\n  } else throw err;\n}","preventionTips":["Throttle requests to eastmoney and add exponential backoff for 429/5xx responses.","Always send a desktop User-Agent header on every call.","Validate date arguments ('YYYY-MM-DD') before building the filter query.","Monitor eastmoney API changes; pin and test the endpoint URL in a smoke test.","Catch CliError by its code property rather than parsing message text."],"tags":["http","network","api","eastmoney"],"backgroundTag":"http-non-2xx-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}