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

  1. Check whether wrapped=true matches the actual API response; if rows are {data:...} envelopes, pass wrapped=true so the inner object is projected.
  2. Log and filter the raw records array before calling projectNowcoderFeed to drop null/primitive rows from deleted posts.
  3. Call fetchNowcoderData to obtain records so the response envelope is validated before projection.
  4. 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

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

Related errors


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