jackwener/OpenCLI · error · CommandExecutionError
Nowcoder returned a malformed feed row
Error message
Nowcoder returned a malformed feed row
What it means
projectFeedData validates each raw feed row returned by Nowcoder's API before projecting it into a normalized post. This error is thrown when a row is not a plain non-null, non-array object (fails the isRecord check), meaning the upstream feed payload contained null, an array, a primitive, or a missing entry where a feed row was expected. The library throws CommandExecutionError to fail fast rather than silently emitting corrupted post data.
Source
Thrown at clis/nowcoder/posts.js:126
}
function commonFeedFields(data, post, postType, authorId, timestamp, index) {
if (!isRecord(data.frequencyData)) throw new CommandExecutionError(`Nowcoder returned malformed ${postType} frequencyData`);
return {
rank: index + 1,
post_type: postType,
title: optionalText(post.title, `${postType} title`) || '(untitled)',
...authorFields(data.userBrief, authorId, postType),
content: cleanBody(post.content, `${postType} content`),
likes: metric(data.frequencyData, 'likeCnt'),
comments: metric(data.frequencyData, 'commentCnt'),
views: metric(data.frequencyData, 'viewCnt'),
time: isoTime(timestamp, `${postType} timestamp`),
};
}
function projectFeedData(data, index, requireOuterId) {
if (!isRecord(data)) throw new CommandExecutionError('Nowcoder returned a malformed feed row');
if (data.contentType === CONTENT_FEED_TYPE) {
if (!isRecord(data.contentData) || data.momentData != null || data.contentData.entityType !== CONTENT_ENTITY_TYPE) {
throw new CommandExecutionError('Nowcoder returned mismatched content feed data');
}
const post = data.contentData;
const id = requiredId(post.id, 'content id');
if ((requireOuterId || data.contentId != null) && id !== requiredId(data.contentId, 'content feed id')) {
throw new CommandExecutionError('Nowcoder returned mismatched content feed identity');
}
const uuid = requiredUuid(post.uuid, 'content uuid');
return {
...commonFeedFields(data, post, 'content', post.authorId, post.createTime, index),
id,
uuid,
entity_id: requiredId(post.entityId, 'content entity id'),
url: `https://www.nowcoder.com/discuss/${id}`,
};
}View on GitHub (pinned to 49907e53dc)
Solutions
- Check whether wrapped=true matches the actual API response; if rows are {data:...} envelopes, pass wrapped=true so the inner object is projected.
- Log and filter the raw records array before calling projectNowcoderFeed to drop null/primitive rows from deleted posts.
- Call fetchNowcoderData to obtain records so the response envelope is validated before projection.
- If Nowcoder changed its schema, update the projection code to the new field layout.
Example fix
// before
projectNowcoderFeed(response.records, 10, 'recommend'); // rows are {data:...}
// after
projectNowcoderFeed(response.records, 10, 'recommend', true); Defensive patterns
Strategy: type-guard
Validate before calling
// before calling projectNowcoderFeed
const isValidRow = (r) => r != null && typeof r === 'object' && !Array.isArray(r);
if (!records.every((r) => isValidRow(wrapped ? r?.data : r))) throw new Error('feed contains malformed rows'); Type guard
function isRecord(value) { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); } Try / catch
try {
rows = projectNowcoderFeed(records, limit, source, wrapped);
} catch (error) {
if (String(error.message).includes('malformed feed row')) {
rows = projectNowcoderFeed(records.filter((r) => isRecord(wrapped ? r?.data : r)), limit, source, wrapped);
} else throw error;
} Prevention
- Always fetch records via fetchNowcoderData so the response envelope is validated first
- Match wrapped=true to APIs that return {data:...} row envelopes
- Pre-filter null/primitive rows (deleted posts) before projection
- Log the raw response when the schema is unfamiliar
When it happens
Trigger: projectNowcoderFeed passes each element of the records array (or record.data when wrapped=true) into projectFeedData; this fires for any element that is null, undefined, an array, or a primitive (string/number/boolean) instead of a feed row object.
Common situations: Nowcoder changes its feed API response shape and omits 'data' on wrapped records; an API page returns null rows for deleted/removed posts; passing your own hand-built array to projectNowcoderFeed with a wrong element type; forgetting wrapped=true when the source returns {data: ...} envelopes so record?.data is undefined.
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
- Nowcoder returned mismatched content feed data
- Nowcoder returned mismatched moment feed data
- Bilibili conclusion API returned malformed model_result JSON
- Bilibili conclusion API returned malformed model_result
- ${label} returned malformed items payload
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/f931afb20089d855.
Report an issue: GitHub.