jackwener/OpenCLI · error · CliError

INVALID_ARGUMENT

INVALID_ARGUMENT

Error message

${err instanceof Error ? err.message : err}

What it means

CliError('INVALID_ARGUMENT') thrown in clis/eastmoney/holders.js when toSecucode(args.symbol) rejects the user-supplied symbol. The CLI wraps the plain Error into a typed CliError so callers can distinguish bad input from network failures. This is the holders command's input-validation gate before any HTTP request is made.

Source

Thrown at clis/eastmoney/holders.js:45

cli({
  site: 'eastmoney',
  name: 'holders',
    access: 'read',
  description: '十大流通股东(A股 F10 数据)',
  domain: 'datacenter-web.eastmoney.com',
  strategy: Strategy.PUBLIC,
  browser: false,
  args: [
    { name: 'symbol', required: true, positional: true, help: 'A股代码(600519 / sh600519 等)' },
    { name: 'limit',  type: 'int',    default: 10,      help: '返回股东数(默认十大流通股东)' },
  ],
  columns: ['rank', 'reportDate', 'name', 'holdNum', 'floatRatio', 'change'],
  func: async (args) => {
    /** @type {string} */
    let secucode;
    try { secucode = toSecucode(args.symbol); }
    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}`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Supply a 6-digit A-share code (e.g. 600519) or prefixed form (1.600519 / 0.000001).
  2. Normalize the symbol first: trim, strip 'sh'/'sz'/'bj' prefixes, convert full-width digits.
  3. Validate with /((^\d\.)|^)\d{6}$/ before invoking the CLI to fail early with your own message.
  4. Branch on CliError code 'INVALID_ARGUMENT' in your wrapper to print usage help instead of a stack trace.
  5. If you need textual prefixes or HK/US symbols, extend toSecucode — the shipped version doesn't accept them.

Example fix

// caller-side validation before the CLI/library call
// before
const holders = await fetchHolders({ symbol: args.symbol });
// after
const symbol = String(args.symbol ?? '').trim().replace(/^(sh|sz|bj)/i, '');
if (!/^\d{6}$/.test(symbol)) throw new CliError('INVALID_ARGUMENT', `--symbol must be a 6-digit A-share code, got: ${args.symbol}`);
const holders = await fetchHolders({ symbol });
Defensive patterns

Strategy: validation

Validate before calling

const symbol = String(args.symbol ?? '').trim().replace(/^(sh|sz|bj)/i, '');
if (!/^\d{6}$/.test(symbol)) {
  throw new CliError('INVALID_ARGUMENT', `--symbol must be a 6-digit A-share code, got: ${args.symbol}`);
}

Type guard

/**
 * @param {unknown} v
 * @returns {v is string}
 */
function isParseableSymbol(v) {
  if (typeof v !== 'string') return false;
  const s = v.trim();
  return /^\d+\.\d{6}$/.test(s) || /^\d{6}$/.test(s);
}

Try / catch

try {
  const holders = await fetchHolders({ symbol: '600519', limit: 10 });
} catch (err) {
  if (err instanceof CliError && err.code === 'INVALID_ARGUMENT') {
    console.error(`Bad --symbol: ${err.message}. Example: holders --symbol 600519`);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Invoking the holders CLI with a --symbol that toSecucode cannot normalize (non-numeric tickers, wrong digit counts, unsupported prefixes) — caught at `catch (err) { throw new CliError('INVALID_ARGUMENT', ...)` at clis/eastmoney/holders.js:45.

Common situations: Passing 'AAPL' or '00700' (HK) instead of a mainland A-share code, using 'sh600519' textual prefixes, or a symbol with hidden whitespace/full-width digits from copy-paste.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/af51f6dbfd146e46. Report an issue: GitHub.