{"record":{"id":"689da1057214f13b","repo":"jackwener/OpenCLI","slug":"fetch-error-689da1","errorCode":"FETCH_ERROR","errorMessage":"Sina API HTTP ${res.status}","messagePattern":"Sina API HTTP (.+?)","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/sinafinance/stock.js","lineNumber":16,"sourceCode":"/**\n * Sinafinance stock quote — A股 / 港股 / 美股\n *\n * Uses two public Sina APIs (no browser required):\n *   suggest3.sinajs.cn  — symbol search\n *   hq.sinajs.cn        — real-time quote\n */\nimport { cli, Strategy } from '@jackwener/opencli/registry';\nimport { CliError } from '@jackwener/opencli/errors';\nconst MARKET_CN = '11';\nconst MARKET_HK = '31';\nconst MARKET_US = '41';\nasync function fetchGBK(url) {\n    const res = await fetch(url, { headers: { Referer: 'https://finance.sina.com.cn' } });\n    if (!res.ok)\n        throw new CliError('FETCH_ERROR', `Sina API HTTP ${res.status}`, 'Check your network');\n    const buf = await res.arrayBuffer();\n    return new TextDecoder('gbk').decode(buf);\n}\nfunction parseSuggest(raw, markets) {\n    const m = raw.match(/suggestvalue=\"(.*)\"/s);\n    if (!m)\n        return [];\n    return m[1].split(';').filter(Boolean).map(s => {\n        const p = s.split(',');\n        return { name: p[4] || p[0] || '', market: p[1] || '', symbol: p[3] || '' };\n    }).filter(e => markets.includes(e.market));\n}\nfunction hqSymbol(e) {\n    if (e.market === MARKET_HK)\n        return `hk${e.symbol}`;\n    if (e.market === MARKET_US)\n        return `gb_${e.symbol}`;\n    return e.symbol; // A股: already \"sh600519\" / \"sz300XXX\"","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/sinafinance/stock.js#L1-L34","documentation":"CliError with code FETCH_ERROR thrown by fetchGBK when a Sina API endpoint (suggest or quote, called via suggestRaw/hqRaw) returns a non-ok HTTP status. The status code is interpolated. fetchGBK sets a Referer header because Sina requires it, so failures usually mean blocking or upstream issues rather than missing headers.","triggerScenarios":"Any call to fetchGBK — symbol suggestion or realtime quote lookups — where the Sina endpoint returns 403 (Referer/bot detection, rate limiting), 404 (endpoint change), or 5xx (outage).","commonSituations":"Hammering suggest/quote endpoints causing 403 rate limits; Sina blocking datacenter IPs; endpoint deprecation after Sina API changes; Sina CDN incidents returning 5xx.","solutions":["Check the interpolated status: 403 → blocked/throttled, 404 → endpoint moved, 5xx → Sina outage","Slow down request rate and add delays between lookups","Ensure requests include the Referer header (the library sets this; custom forks must too)","For 404, verify the current Sina endpoint URLs and update the code"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const stock = await cli.stock({ key, market });\n} catch (err) {\n  if (err.code === 'FETCH_ERROR') {\n    const m = err.message.match(/HTTP (\\d+)/);\n    if (m && m[1] === '403') await sleep(30_000);   // throttled/blocked\n    else if (m && Number(m[1]) >= 500) return retryLater();\n    throw err;\n  }\n  throw err;\n}","preventionTips":["Keep request volume low; Sina aggressively rate-limits suggest/quote endpoints","Never strip the Referer header (https://finance.sina.com.cn) if forking","Expect 403 from datacenter IPs; run from residential/allowed networks","Distinguish FETCH_ERROR (HTTP) from network failures before retrying"],"tags":["http-status","sina","rate-limit"],"backgroundTag":"http-4xx-5xx-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}