jackwener/OpenCLI · error · CommandExecutionError

eastmoney convertible returned a malformed data envelope

Error message

eastmoney convertible returned a malformed data envelope

What it means

Thrown by extractConvertibleDiff (clis/eastmoney/convertible.js:64) when the top-level object parsed from the eastmoney push2 response lacks a valid `data` sub-object (missing, null, a primitive, or an array). The push2 clist API normally nests the result rows at `data.diff`, so the CLI requires `data` to be a plain object before reading `.diff`. This is a CommandExecutionError indicating the API contract changed or the response was truncated/empty.

Source

Thrown at clis/eastmoney/convertible.js:64

export function parseConvertibleLimit(value) {
  if (value === undefined || value === null || value === '') return 20;
  if (typeof value === 'number') {
    if (Number.isInteger(value) && value >= 1 && value <= 100) return value;
    throw new ArgumentError('eastmoney convertible --limit must be an integer between 1 and 100');
  }
  const raw = String(value).trim();
  if (!/^\d+$/.test(raw)) throw new ArgumentError('eastmoney convertible --limit must be an integer between 1 and 100');
  const parsed = Number(raw);
  if (parsed < 1 || parsed > 100) throw new ArgumentError('eastmoney convertible --limit must be an integer between 1 and 100');
  return parsed;
}

export function extractConvertibleDiff(data) {
  if (!data || typeof data !== 'object' || Array.isArray(data)) {
    throw new CommandExecutionError('eastmoney convertible returned a malformed response envelope');
  }
  if (!data.data || typeof data.data !== 'object' || Array.isArray(data.data)) {
    throw new CommandExecutionError('eastmoney convertible returned a malformed data envelope');
  }
  const diff = data.data.diff;
  if (!Array.isArray(diff)) {
    throw new CommandExecutionError('eastmoney convertible returned malformed diff data');
  }
  if (diff.length === 0) {
    throw new EmptyResultError('eastmoney convertible');
  }
  return diff;
}

// Map a raw eastmoney clist `diff` item to an output row.
//
// #2109: f238 / f239 were previously emitted as `remainingYears` / `ytm`, but
// cross-verification (12/12 fingerprint hits) shows f239 is the putback trigger
// price (= convPrice × 0.7) and f238 is the pure-bond premium %. Real YTM /
// remaining term are not in this response's `fields`; adding the correct f-codes
// is a follow-up that needs a live push2 field dump cross-checked against jisilu.

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the request; soft-empty responses are frequently transient.
  2. Check HTTP status was 200 and log the raw body to confirm `data` is genuinely absent rather than an auth rejection.
  3. Verify the `ut` token and `fs=b:MK0354` query params are still valid against the live push2 API.
  4. Try a different network/User-Agent if a soft block is suspected.
  5. Guard in caller code: check `data && data.data && typeof data.data === 'object'` before extracting diff.

Example fix

// before
const diff = extractConvertibleDiff(await resp.json());
// after
const json = await resp.json();
if (!json?.data || typeof json.data !== 'object' || Array.isArray(json.data)) {
  throw new Error('eastmoney response missing data envelope: ' + JSON.stringify(json).slice(0, 200));
}
const diff = extractConvertibleDiff(json);
Defensive patterns

Strategy: validation

Validate before calling

if (!json || typeof json !== 'object' || Array.isArray(json) || !json.data || typeof json.data !== 'object' || Array.isArray(json.data)) {
  throw new Error('eastmoney response missing data envelope');
}

Type guard

function hasDataEnvelope(v) {
  return v !== null && typeof v === 'object' && !Array.isArray(v) &&
    v.data !== null && typeof v.data === 'object' && !Array.isArray(v.data);
}

Try / catch

try {
  const diff = extractConvertibleDiff(json);
} catch (e) {
  if (String(e.message).includes('malformed data envelope')) {
    console.warn('empty/soft-blocked response, retrying once');
    return retryWithBackoff(fetchConvertible);
  }
  throw e;
}

Prevention

When it happens

Trigger: The eastmoney endpoint returns `{}` or `{ rc:..., data: null }` — typical when the `fs=b:MK0354` board has no data for the query, the `ut` token is rejected, or the request was rate-limited into an empty success (HTTP 200 with an empty payload).

Common situations: Calling with sort/limit combinations the backend refuses silently; eastmoney rotating or invalidating the hard-coded `ut` token; regional restrictions returning empty envelopes; scraping from a datacenter IP that gets soft-blocked with an empty 200.

Understand the failure class

Related errors


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