jackwener/OpenCLI · warning · CliError
NO_DATA
NO_DATA
Error message
No shareholder data for ${secucode} What it means
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.
Source
Thrown at clis/eastmoney/holders.js:63
catch (err) { throw new CliError('INVALID_ARGUMENT', `${err instanceof Error ? err.message : err}`); }
const limit = Math.max(1, Math.min(Number(args.limit) || 10, 50));
const url = new URL('https://datacenter-web.eastmoney.com/api/data/v1/get');
url.searchParams.set('sortColumns', 'END_DATE,HOLDER_RANK');
url.searchParams.set('sortTypes', '-1,1');
url.searchParams.set('pageSize', String(Math.max(limit, 10)));
url.searchParams.set('pageNumber', '1');
url.searchParams.set('reportName', 'RPT_F10_EH_FREEHOLDERS');
url.searchParams.set('columns', 'SECUCODE,SECURITY_CODE,END_DATE,HOLDER_RANK,HOLDER_NAME,HOLD_NUM,FREE_HOLDNUM_RATIO,HOLD_NUM_CHANGE');
url.searchParams.set('source', 'HSF10');
url.searchParams.set('client', 'PC');
url.searchParams.set('filter', `(SECUCODE="${secucode}")`);
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
if (!resp.ok) throw new CliError('HTTP_ERROR', `holders failed: HTTP ${resp.status}`);
const data = await resp.json();
const rows = Array.isArray(data?.result?.data) ? data.result.data : [];
if (rows.length === 0) throw new CliError('NO_DATA', `No shareholder data for ${secucode}`);
// Only the most recent reporting period
const latest = String(rows[0].END_DATE || '').slice(0, 10);
return rows
.filter((it) => String(it.END_DATE || '').slice(0, 10) === latest)
.slice(0, limit)
.map((it) => ({
rank: it.HOLDER_RANK,
reportDate: latest,
name: it.HOLDER_NAME,
holdNum: it.HOLD_NUM,
floatRatio: it.FREE_HOLDNUM_RATIO,
change: it.HOLD_NUM_CHANGE,
}));
},
});
View on GitHub (pinned to 49907e53dc)
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
Example fix
// before
getHolders('0.301999') // throws NO_DATA for unknown/delisted code
// after
try {
const holders = await getHolders('0.301999');
} catch (e) {
if (e.code === 'NO_DATA') return []; // treat as empty, not fatal
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
function isValidSecucode(code) {
return typeof code === 'string' && /^\d\.\d{6}$/.test(code);
}
// also skip known-delisted symbols before calling Type guard
function hasHolderRows(data) {
return Array.isArray(data?.result?.data) && data.result.data.length > 0;
} Try / catch
try {
const rows = await getHolders(secucode);
render(rows);
} catch (err) {
if (err.code === 'NO_DATA') {
render([]); // expected for new/delisted securities
} else {
throw err;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/eb7103f8f522f943.
Report an issue: GitHub.