jackwener/OpenCLI · error · CommandExecutionError
eastmoney convertible returned malformed row at rank ${rank}
Error message
eastmoney convertible returned malformed row at rank ${rank} What it means
Thrown by mapConvertibleRow (clis/eastmoney/convertible.js:85) when a single `diff` element is not a plain object (null, primitive, or array). Each diff item is expected to be an object keyed by eastmoney field codes (f12, f14, f232, ...). The error includes the 1-based rank so the offending row can be located. It is a CommandExecutionError from the row-mapping layer, meaning the envelope passed but a row inside is corrupt.
Source
Thrown at clis/eastmoney/convertible.js:85
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.
export function mapConvertibleRow(it, rank) {
if (!it || typeof it !== 'object' || Array.isArray(it)) {
throw new CommandExecutionError(`eastmoney convertible returned malformed row at rank ${rank}`);
}
const bondCode = normalizeEastmoneyIdentityString(it.f12, 'f12', '');
const bondName = normalizeEastmoneyIdentityString(it.f14, 'f14', bondCode);
const stockCode = normalizeEastmoneyIdentityString(it.f232, 'f232', bondCode);
const stockName = normalizeEastmoneyIdentityString(it.f234, 'f234', bondCode);
for (const field of NUMERIC_FIELDS) {
normalizeEastmoneyNumeric(it[field], field, bondCode);
}
return {
rank,
bondCode,
bondName,
bondPrice: normalizeEastmoneyNumeric(it.f2, 'f2', bondCode),
bondChangePct: normalizeEastmoneyNumeric(it.f3, 'f3', bondCode),
stockCode,
stockName,
stockPrice: normalizeEastmoneyNumeric(it.f229, 'f229', bondCode),
stockChangePct: normalizeEastmoneyNumeric(it.f230, 'f230', bondCode),View on GitHub (pinned to 49907e53dc)
Solutions
- Filter non-object entries before mapping: `diff.filter(it => it && typeof it === 'object' && !Array.isArray(it))`.
- Inspect the raw response at the reported rank to see what eastmoney actually returned.
- Update test fixtures to contain only plain-object rows.
- Re-fetch — a transiently corrupt payload usually differs on retry.
- If eastmoney systematically emits null rows, add a skip-with-warning instead of throwing.
Example fix
// before return capped.map((it, i) => mapConvertibleRow(it, i + 1)); // after return capped .filter(it => it && typeof it === 'object' && !Array.isArray(it)) .map((it, i) => mapConvertibleRow(it, i + 1));
Defensive patterns
Strategy: validation
Validate before calling
const cleanRows = diff.filter(it => it && typeof it === 'object' && !Array.isArray(it)); const rows = mapConvertibleRows(cleanRows, limit);
Type guard
function isConvertibleRow(it) {
return it !== null && typeof it === 'object' && !Array.isArray(it) &&
typeof it.f12 === 'string' && it.f12.trim() !== '';
} Try / catch
try {
return mapConvertibleRows(diff, limit);
} catch (e) {
const m = String(e.message).match(/malformed row at rank (\d+)/);
if (m) {
console.warn(`skipping corrupt row at rank ${m[1]}`);
return mapConvertibleRows(diff.filter(isConvertibleRow), limit);
}
throw e;
} Prevention
- Filter diff rows for plain objects before mapping.
- Keep null-tolerant handling for suspended/delisted bonds eastmoney pads with nulls.
- Validate fixtures row-by-row in tests.
- Log the offending rank's raw item when the error fires.
When it happens
Trigger: mapConvertibleRows slices the diff array and maps each item; any element that is null or non-object triggers this with `rank = index + 1`. Happens when eastmoney pads diff with nulls, when fixtures are malformed, or when a JSON transform converts rows unexpectedly.
Common situations: Eastmoney returning sparse/null placeholder rows for suspended bonds; test fixtures containing nulls; response post-processing (e.g. CSV-ish serializers) flattening rows; calling mapConvertibleRow directly with wrong arguments in unit tests.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- eastmoney convertible returned a malformed response envelope
- No user message found in request
- 获取到的视频播放信息对象不符合预期格式
- Bilibili view API returned a malformed ${label}
- Bilibili view API did not return pages[] for --page selectio
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/b9dec4ae0b314427.
Report an issue: GitHub.