{"record":{"id":"ad4cc7fe5b715ece","repo":"jackwener/OpenCLI","slug":"http-error-ad4cc7","errorCode":"HTTP_ERROR","errorMessage":"announcement failed: HTTP ${resp.status}","messagePattern":"announcement failed: HTTP (.+?)","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/eastmoney/announcement.js","lineNumber":35,"sourceCode":"  args: [\n    { name: 'market', type: 'string', default: 'SHA,SZA,BJA', help: '交易所：SHA (沪) / SZA (深) / BJA (北) 可逗号分隔' },\n    { name: 'limit',  type: 'int',    default: 20,            help: '返回数量 (max 100)' },\n  ],\n  columns: ['time', 'code', 'name', 'title', 'category', 'url'],\n  func: async (args) => {\n    const market = String(args.market ?? 'SHA,SZA,BJA').trim() || 'SHA,SZA,BJA';\n    const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));\n\n    const url = new URL('https://np-anotice-stock.eastmoney.com/api/security/ann');\n    url.searchParams.set('page_size', String(limit));\n    url.searchParams.set('page_index', '1');\n    url.searchParams.set('ann_type', market);\n    url.searchParams.set('client_source', 'web');\n    url.searchParams.set('f_node', '0');\n    url.searchParams.set('s_node', '0');\n\n    const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\n    if (!resp.ok) throw new CliError('HTTP_ERROR', `announcement failed: HTTP ${resp.status}`);\n    const data = await resp.json();\n    const list = Array.isArray(data?.data?.list) ? data.data.list : [];\n    if (list.length === 0) throw new CliError('NO_DATA', 'eastmoney returned no announcement data');\n\n    return list.slice(0, limit).map((it) => {\n      const primary = Array.isArray(it.codes) && it.codes.length > 0 ? it.codes[0] : {};\n      const cat = Array.isArray(it.columns) && it.columns.length > 0 ? it.columns[0]?.column_name : '';\n      return {\n        time: String(it.notice_date || it.display_time || '').slice(0, 19),\n        code: primary.stock_code || '',\n        name: primary.short_name || '',\n        title: it.title || it.title_ch || '',\n        category: cat || '',\n        url: `https://data.eastmoney.com/notices/detail/${primary.stock_code || ''}/${it.art_code || ''}.html`,\n      };\n    });\n  },\n});","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/eastmoney/announcement.js#L17-L53","documentation":"The announcement fetch to Eastmoney's web API returned a non-2xx HTTP status. The code checks resp.ok after the fetch and wraps the status into a CliError with code HTTP_ERROR. This is a server/endpoint-level failure, not a parsing problem.","triggerScenarios":"Eastmoney returns 4xx/5xx for the announcement endpoint (rate limiting, temporary outage, WAF/anti-bot block, invalid query params causing 400). Any fetch where !resp.ok triggers this.","commonSituations":"Hammering the endpoint in a loop triggers rate limiting or an anti-scraping block; Eastmoney changes/retires the endpoint; a corporate proxy returns 403/502; transient 5xx during market-hours load.","solutions":["Log resp.status, fix the obvious cause (429 → back off and slow down; 403 → change IP/headers; 5xx → retry later).","Retry with exponential backoff for transient 5xx/429.","Refresh the User-Agent/headers to look like a real browser if blocked.","Verify the endpoint URL and query params are still valid against the current Eastmoney web API."],"exampleFix":"// before\nconst resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\nif (!resp.ok) throw new CliError('HTTP_ERROR', `announcement failed: HTTP ${resp.status}`);\n// after\nfor (let i = 0; i < 3; i++) {\n  const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\n  if (resp.ok) return handle(await resp.json());\n  if (resp.status === 429 || resp.status >= 500) await new Promise(r => setTimeout(r, 2 ** i * 1000));\n  else throw new CliError('HTTP_ERROR', `announcement failed: HTTP ${resp.status}`);\n}","handlingStrategy":"retry","validationCode":"// no pre-call validation possible for remote HTTP status; ensure URL/params are well-formed:\nconst u = new URL('https://np-anotice-stock.eastmoney.com/api/security/ann');\n// verify url.toString() and params before fetching","typeGuard":"null","tryCatchPattern":"try {\n  rows = await getAnnouncements(secid);\n} catch (e) {\n  if (e?.code === 'HTTP_ERROR' && /HTTP (429|5\\d\\d)/.test(e.message)) {\n    await backoff(); rows = await getAnnouncements(secid); // retry transient failures\n  } else if (e?.code === 'HTTP_ERROR') {\n    console.error(`Eastmoney rejected the request (${e.message}); check endpoint/params/headers`);\n  } else throw e;\n}","preventionTips":["Retry 429/5xx with exponential backoff and jitter","Rate-limit calls to avoid anti-scraping blocks","Keep the User-Agent and headers browser-like and current","Monitor for Eastmoney endpoint changes"],"tags":["network","http","api","eastmoney"],"backgroundTag":"http-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}