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

  1. Verify the secucode format and market prefix (e.g. SH: 1.600000, SZ: 0.000001) is correct for the security
  2. Check the security still trades on Eastmoney's web UI (quote page) to confirm data exists at all
  3. For new IPOs, wait until the first shareholder disclosure is published
  4. 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

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.