{"record":{"id":"eb7103f8f522f943","repo":"jackwener/OpenCLI","slug":"no-data-eb7103","errorCode":"NO_DATA","errorMessage":"No shareholder data for ${secucode}","messagePattern":"No shareholder data for (.+?)","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"warning","filePath":"clis/eastmoney/holders.js","lineNumber":63,"sourceCode":"    catch (err) { throw new CliError('INVALID_ARGUMENT', `${err instanceof Error ? err.message : err}`); }\n    const limit = Math.max(1, Math.min(Number(args.limit) || 10, 50));\n\n    const url = new URL('https://datacenter-web.eastmoney.com/api/data/v1/get');\n    url.searchParams.set('sortColumns', 'END_DATE,HOLDER_RANK');\n    url.searchParams.set('sortTypes', '-1,1');\n    url.searchParams.set('pageSize', String(Math.max(limit, 10)));\n    url.searchParams.set('pageNumber', '1');\n    url.searchParams.set('reportName', 'RPT_F10_EH_FREEHOLDERS');\n    url.searchParams.set('columns', 'SECUCODE,SECURITY_CODE,END_DATE,HOLDER_RANK,HOLDER_NAME,HOLD_NUM,FREE_HOLDNUM_RATIO,HOLD_NUM_CHANGE');\n    url.searchParams.set('source', 'HSF10');\n    url.searchParams.set('client', 'PC');\n    url.searchParams.set('filter', `(SECUCODE=\"${secucode}\")`);\n\n    const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\n    if (!resp.ok) throw new CliError('HTTP_ERROR', `holders failed: HTTP ${resp.status}`);\n    const data = await resp.json();\n    const rows = Array.isArray(data?.result?.data) ? data.result.data : [];\n    if (rows.length === 0) throw new CliError('NO_DATA', `No shareholder data for ${secucode}`);\n\n    // Only the most recent reporting period\n    const latest = String(rows[0].END_DATE || '').slice(0, 10);\n    return rows\n      .filter((it) => String(it.END_DATE || '').slice(0, 10) === latest)\n      .slice(0, limit)\n      .map((it) => ({\n        rank: it.HOLDER_RANK,\n        reportDate: latest,\n        name: it.HOLDER_NAME,\n        holdNum: it.HOLD_NUM,\n        floatRatio: it.FREE_HOLDNUM_RATIO,\n        change: it.HOLD_NUM_CHANGE,\n      }));\n  },\n});\n","sourceCodeStart":45,"sourceCodeEnd":80,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/eastmoney/holders.js#L45-L80","documentation":"This CliError with code NO_DATA is thrown when the Eastmoney holders API returns a successful HTTP response but the result payload contains no shareholder rows for the requested secucode. The library normalizes data.result.data to an array; if it is missing, not an array, or empty, it cannot produce shareholder data and throws instead of returning an empty result. It means the request was well-formed and delivered, but Eastmoney has no rows for that security.","triggerScenarios":"Calling the holders CLI/tool with a secucode that has no shareholder records in Eastmoney's database: delisted or suspended securities, brand-new IPOs before the first disclosure, invalid but well-formed codes (e.g. wrong market prefix), or Eastmoney returning {result: null} for the (SECUCODE=\"...\") filter.","commonSituations":"Querying a recently listed stock before its first shareholder disclosure; typos in the secucode market segment (e.g. 0.xxxxxx vs 1.xxxxxx); querying delisted tickers scraped from old datasets; Eastmoney silently returning an empty result instead of an HTTP error for unknown codes.","solutions":["Verify the secucode format and market prefix (e.g. SH: 1.600000, SZ: 0.000001) is correct for the security","Check the security still trades on Eastmoney's web UI (quote page) to confirm data exists at all","For new IPOs, wait until the first shareholder disclosure is published","Treat NO_DATA as an expected empty result in callers and fall back to another data source or skip the security"],"exampleFix":"// before\ngetHolders('0.301999') // throws NO_DATA for unknown/delisted code\n// after\ntry {\n  const holders = await getHolders('0.301999');\n} catch (e) {\n  if (e.code === 'NO_DATA') return []; // treat as empty, not fatal\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"function isValidSecucode(code) {\n  return typeof code === 'string' && /^\\d\\.\\d{6}$/.test(code);\n}\n// also skip known-delisted symbols before calling","typeGuard":"function hasHolderRows(data) {\n  return Array.isArray(data?.result?.data) && data.result.data.length > 0;\n}","tryCatchPattern":"try {\n  const rows = await getHolders(secucode);\n  render(rows);\n} catch (err) {\n  if (err.code === 'NO_DATA') {\n    render([]); // expected for new/delisted securities\n  } else {\n    throw err;\n  }\n}","preventionTips":["Validate secucode format (market digit + 6 digits) before calling","Check the security exists and trades on Eastmoney before querying holders","Handle NO_DATA as an empty dataset, not a crash, in batch jobs","Log the secucode with the error so bad codes are easy to audit"],"tags":["empty-result","api","no-data","eastmoney"],"backgroundTag":"empty-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}