{"record":{"id":"e395973dd53e7020","repo":"jackwener/OpenCLI","slug":"nowcoder-returned-mismatched-content-feed-identity","errorCode":null,"errorMessage":"Nowcoder returned mismatched content feed identity","messagePattern":"Nowcoder returned mismatched content feed identity","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/nowcoder/posts.js","lineNumber":134,"sourceCode":"        ...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');\n        }\n        const post = data.momentData;\n        const entityId = requiredId(post.id, 'moment entity id');\n        if ((requireOuterId || data.contentId != null) && entityId !== requiredId(data.contentId, 'moment feed id')) {\n            throw new CommandExecutionError('Nowcoder returned mismatched moment feed identity');","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/nowcoder/posts.js#L116-L152","documentation":"For content-type feed rows, the inner post id must match the outer row's contentId whenever the outer id is required (experience source or wrapped mode) or present. This error means contentData.id and data.contentId disagree, indicating the feed row's payload does not correspond to the id Nowcoder advertised at the envelope level, so the library treats it as inconsistent data.","triggerScenarios":"A contentType-250 row where (requireOuterId || data.contentId != null) and requiredId(post.id) !== requiredId(data.contentId) — i.e., inner content id differs from the outer contentId (or contentId is absent when required).","commonSituations":"Nowcoder reuses feed envelopes for recommended/related items whose outer id points elsewhere; experience feed returns wrapped rows without contentId; caching mixed rows from different requests; upstream id reassignment after post edits.","solutions":["Check data.contentId vs post.id on the offending row to confirm which id is authoritative.","If the source does not guarantee outer ids, avoid forcing requireOuterId (don't pass source='experience' or wrapped=true unnecessarily).","Pre-filter rows where contentData.id !== contentId before projection.","Re-fetch the feed; if it reproduces consistently, Nowcoder changed envelope semantics and the code must be updated."],"exampleFix":"// before\nrows.push(projectFeedData(data, rows.length, true)); // forces outer id check\n// after\nconst consistent = data.contentId == null || String(data.contentData?.id) === String(data.contentId);\nif (consistent) rows.push(projectFeedData(data, rows.length, true));","handlingStrategy":"validation","validationCode":"const idsMatch = (d) => d?.contentId == null || String(d?.contentData?.id) === String(d.contentId);\nconst usable = records.filter((r) => { const d = wrapped ? r?.data : r; return d?.contentType !== 250 || idsMatch(d); });","typeGuard":"function hasConsistentContentIdentity(d) { return isRecord(d) && (d.contentId == null || String(d.contentData?.id) === String(d.contentId)); }","tryCatchPattern":"try {\n    rows = projectNowcoderFeed(records, limit, source, wrapped);\n} catch (error) {\n    if (String(error.message).includes('mismatched content feed identity')) {\n        rows = projectNowcoderFeed(records.filter((r) => hasConsistentContentIdentity(wrapped ? r?.data : r)), limit, source, wrapped);\n    } else throw error;\n}","preventionTips":["Only force outer-id checks (wrapped/source='experience') when the API actually guarantees contentId","Pre-filter rows where contentData.id !== contentId","Do not mix rows cached from different requests","Re-fetch the feed if a single row fails — it may be transient"],"tags":["feed-parsing","data-integrity","id-mismatch"],"backgroundTag":"id-mismatch-between-envelope-and-payload","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}