{"record":{"id":"6e8cb0f62b97d18e","repo":"jackwener/OpenCLI","slug":"nowcoder-returned-mismatched-content-feed-data","errorCode":null,"errorMessage":"Nowcoder returned mismatched content feed data","messagePattern":"Nowcoder returned mismatched content feed data","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/nowcoder/posts.js","lineNumber":129,"sourceCode":"    if (!isRecord(data.frequencyData)) throw new CommandExecutionError(`Nowcoder returned malformed ${postType} frequencyData`);\n    return {\n        rank: index + 1,\n        post_type: postType,\n        title: optionalText(post.title, `${postType} title`) || '(untitled)',\n        ...authorFields(data.userBrief, authorId, postType),\n        content: cleanBody(post.content, `${postType} content`),\n        likes: metric(data.frequencyData, 'likeCnt'),\n        comments: metric(data.frequencyData, 'commentCnt'),\n        views: metric(data.frequencyData, 'viewCnt'),\n        time: isoTime(timestamp, `${postType} timestamp`),\n    };\n}\n\nfunction projectFeedData(data, index, requireOuterId) {\n    if (!isRecord(data)) throw new CommandExecutionError('Nowcoder returned a malformed feed row');\n    if (data.contentType === CONTENT_FEED_TYPE) {\n        if (!isRecord(data.contentData) || data.momentData != null || data.contentData.entityType !== CONTENT_ENTITY_TYPE) {\n            throw new CommandExecutionError('Nowcoder returned mismatched content feed data');\n        }\n        const post = data.contentData;\n        const id = requiredId(post.id, 'content id');\n        if ((requireOuterId || data.contentId != null) && id !== requiredId(data.contentId, 'content feed id')) {\n            throw new CommandExecutionError('Nowcoder returned mismatched content feed identity');\n        }\n        const uuid = requiredUuid(post.uuid, 'content uuid');\n        return {\n            ...commonFeedFields(data, post, 'content', post.authorId, post.createTime, index),\n            id,\n            uuid,\n            entity_id: requiredId(post.entityId, 'content entity id'),\n            url: `https://www.nowcoder.com/discuss/${id}`,\n        };\n    }\n    if (data.contentType === MOMENT_TYPE) {\n        if (!isRecord(data.momentData) || data.contentData != null) {\n            throw new CommandExecutionError('Nowcoder returned mismatched moment feed data');","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/nowcoder/posts.js#L111-L147","documentation":"When a feed row declares contentType 250 (content/discuss post), projectFeedData requires contentData to be a record, momentData to be absent, and contentData.entityType to equal 8. This error means the row's type marker says 'content' but its payload does not match that shape, so the library refuses to project it rather than produce a half-populated post.","triggerScenarios":"A feed row with contentType===250 arrives where contentData is missing/null/not an object, momentData is also present, or contentData.entityType !== 8.","commonSituations":"Nowcoder A/B-testing new feed payloads; pinned/advertised rows that reuse contentType 250 with a different entity type; partial API responses after upstream errors; stale assumptions after a Nowcoder API version change.","solutions":["Inspect the offending row's contentData and entityType to see what shape Nowcoder actually returned.","Skip such rows by pre-filtering records before calling projectNowcoderFeed.","Update CONTENT_ENTITY_TYPE or the projection logic if Nowcoder changed the entity type constant.","Retry the feed request — transient partial payloads may resolve on a fresh fetch."],"exampleFix":"// before\nconst rows = projectNowcoderFeed(records, 10, 'recommend');\n// after\nconst valid = records.filter((r) => r?.data?.contentType !== 250 || r?.data?.contentData?.entityType === 8);\nconst rows = projectNowcoderFeed(valid, 10, 'recommend');","handlingStrategy":"validation","validationCode":"const isContentRow = (d) => isRecord(d) && d.contentType === 250 && isRecord(d.contentData) && d.momentData == null && d.contentData.entityType === 8;\nrecords = records.filter((r) => { const d = wrapped ? r?.data : r; return d == null || isContentRow(d) || d.contentType !== 250; });","typeGuard":"function isProjectableContentRow(d) { return isRecord(d) && d.contentType === 250 && isRecord(d.contentData) && d.momentData == null && d.contentData.entityType === 8; }","tryCatchPattern":"try {\n    rows = projectNowcoderFeed(records, limit, source, wrapped);\n} catch (error) {\n    if (String(error.message).includes('mismatched content feed data')) {\n        console.error('Nowcoder content row shape changed; inspect contentData/entityType');\n    }\n    throw error;\n}","preventionTips":["Verify CONTENT_FEED_TYPE (250) and CONTENT_ENTITY_TYPE (8) still match the live API","Pin/monitor Nowcoder API changes in tests with recorded fixtures","Pre-filter rows whose contentData is missing or whose entityType differs","Retry once on transient partial payloads before failing"],"tags":["feed-parsing","api-response","validation"],"backgroundTag":"api-response-schema-changed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}