jackwener/OpenCLI · error · CommandExecutionError
${label} returned a malformed article id
Error message
${label} returned a malformed article id What it means
readArticleId in clis/juejin/utils.js:144 coerces a value to a trimmed string and validates it against JUEJIN_ID (/^\d{16,20}$/) because Juejin content IDs are 19-digit numeric strings. If the article_id from the API row is missing, null, or not a 16-20 digit number, this CommandExecutionError is thrown to prevent building broken https://juejin.cn/post/<id> links.
Source
Thrown at clis/juejin/utils.js:144
}
export function readDataArray(payload, label) {
if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !Object.hasOwn(payload, 'data')) {
throw new CommandExecutionError(`${label} returned a malformed payload`);
}
if (!Array.isArray(payload.data)) {
throw new CommandExecutionError(`${label} returned a non-array data field`);
}
if (payload.data.length === 0) {
throw new EmptyResultError(label, `${label} returned no articles.`);
}
return payload.data;
}
function readArticleId(value, label) {
const id = String(value ?? '').trim();
if (!JUEJIN_ID.test(id)) {
throw new CommandExecutionError(`${label} returned a malformed article id`);
}
return id;
}
function readOptionalNumber(value, label) {
if (value == null) return null;
const n = Number(value);
if (!Number.isFinite(n)) {
throw new CommandExecutionError(`${label} returned a malformed numeric field`);
}
return n;
}
/** Map a recommend-feed row (`item_info.article_info` / `author_user_info`) to a flat shape. */
export function mapFeedItem(row, rank) {
const info = row?.item_info ?? {};
const article = info.article_info ?? {};
const author = info.author_user_info ?? {};View on GitHub (pinned to 49907e53dc)
Solutions
- Dump the offending row (item_info / content object) to see where the id actually lives after an API change.
- Filter out non-article rows before mapping: skip rows without item_info.article_info (feed) or with non-numeric content_id (hot).
- Update mapFeedItem/mapHotItem field paths if Juejin renamed or moved the id field.
- Wrap per-row mapping in try/catch and skip (or log) rows that fail id validation instead of failing the whole command.
Example fix
// before: one bad row aborts the whole listing
const items = rows.map((row, i) => mapFeedItem(row, i + 1));
// after: skip rows without a valid article id
const items = rows
.filter(row => /^\d{16,20}$/.test(String(row?.item_info?.article_info?.article_id ?? '')))
.map((row, i) => mapFeedItem(row, i + 1)); Defensive patterns
Strategy: validation
Validate before calling
const JUEJIN_ID = /^\d{16,20}$/;
rows = rows.filter(r =>
JUEJIN_ID.test(String(r?.item_info?.article_info?.article_id ?? r?.content?.content_id ?? '').trim())
); Type guard
function hasValidArticleId(row) {
const id = String(row?.item_info?.article_info?.article_id
?? row?.content?.content_id ?? '').trim();
return /^\d{16,20}$/.test(id);
} Try / catch
try {
const items = rows.map((row, i) => mapFeedItem(row, i + 1));
} catch (err) {
if (err instanceof CommandExecutionError && err.message.includes('malformed article id')) {
console.error('Skipping feed row without a valid article id (ad/special card?).');
return;
}
throw err;
} Prevention
- Filter feed rows for a numeric 16-20 digit article_id before mapping.
- Expect ad/special cards in the recommend feed lacking article_info and skip them.
- Validate ids with JUEJIN_ID (/^\d{16,20}$/) at every ingestion boundary, not just display time.
- Re-check field paths (article_info.article_id vs content.content_id) whenever Juejin changes an endpoint.
When it happens
Trigger: A feed or hot-list row where item_info.article_info.article_id (recommend) or content.content_id (hot) is absent, null, non-numeric, or has the wrong digit count — e.g. an empty row, a promoted/non-article item in the feed, or an API shape change moving the id field.
Common situations: Juejin inserting ad or special cards into the recommend feed lacking article_info; the hot-list endpoint returning entries with a different content type whose content_id is not a 19-digit article id; an API contract change relocating article_id.
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
- ${label} returned a malformed payload
- Bilibili conclusion API returned malformed model_result
- eastmoney convertible returned a malformed response envelope
- eastmoney convertible returned malformed diff data
- Flomo API returned a malformed memo entry
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/d24e7339607bb252.
Report an issue: GitHub.