jackwener/OpenCLI · error · CommandExecutionError

eastmoney convertible returned a malformed response envelope

Error message

eastmoney convertible returned a malformed response envelope

What it means

Thrown by extractConvertibleDiff (clis/eastmoney/convertible.js:61) when the value returned from the eastmoney push2 clist API is not a plain object. The library expects the standard eastmoney envelope `{ data: { diff: [...] } }`; anything else (null, an array, a string, HTML text parsed leniently) fails this first envelope check. It is a CommandExecutionError, meaning the remote API response shape violated the contract the CLI relies on.

Source

Thrown at clis/eastmoney/convertible.js:61

  throw new CommandExecutionError(`eastmoney convertible returned malformed ${field} for ${bondCode || 'unknown bond'}`);
}

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

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command; transient anti-bot blocks often clear on retry (ideally with a browser-like User-Agent).
  2. Verify the endpoint still returns JSON by curling https://push2.eastmoney.com/api/qt/clist/get with the same query params (fs=b:MK0354, ut=...).
  3. Check for proxy/VPN interference that replaces the JSON body with an HTML page.
  4. If eastmoney changed the envelope format, update extractConvertibleDiff to the new shape.
  5. In code, validate the response is a non-array object before calling extractConvertibleDiff.

Example fix

// before
const data = await resp.json();
const diff = extractConvertibleDiff(data);
// after
const data = await resp.json();
if (!data || typeof data !== 'object' || Array.isArray(data)) {
  throw new Error('unexpected eastmoney response, not a JSON object');
}
const diff = extractConvertibleDiff(data);
Defensive patterns

Strategy: type-guard

Validate before calling

function isPlainObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }
if (!isPlainObject(response)) throw new Error('eastmoney response is not an object');

Type guard

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

Try / catch

try {
  const diff = extractConvertibleDiff(data);
} catch (e) {
  if (String(e.message).includes('malformed response envelope')) {
    console.error('eastmoney returned a non-object body — likely blocked or changed API');
    return fallbackFetch();
  }
  throw e;
}

Prevention

When it happens

Trigger: extractConvertibleDiff(data) is called with data that is null/undefined, a primitive, or an Array — e.g. the push2 endpoint returned `{}`-like content, an HTML error/anti-bot page that somehow parsed, or a proxy stripped the body. Callers: the `diff` step inside the `eastmoney convertible` cli func after `resp.json()` succeeds.

Common situations: Eastmoney WAF/anti-bot interception returning a non-API body; region-blocked or rate-limited responses returning empty payloads; a corporate proxy or captive portal injecting an HTML page; eastmoney changing or deprecating the `/api/qt/clist/get` response format; unit tests feeding mock data of the wrong shape.

Understand the failure class

Related errors


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