{"record":{"id":"43f48d2eefac9669","repo":"jackwener/OpenCLI","slug":"nowcoder-returned-mismatched-label-authorship","errorCode":null,"errorMessage":"Nowcoder returned mismatched ${label} authorship","messagePattern":"Nowcoder returned mismatched (.+?) authorship","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/nowcoder/posts.js","lineNumber":101,"sourceCode":"}\n\nfunction isoTime(value, label) {\n    if (!Number.isSafeInteger(value) || value <= 0) throw new CommandExecutionError(`Nowcoder returned a malformed ${label}`);\n    const date = new Date(value);\n    if (Number.isNaN(date.getTime())) throw new CommandExecutionError(`Nowcoder returned a malformed ${label}`);\n    return date.toISOString();\n}\n\nfunction metric(frequency, key) {\n    const value = frequency[key];\n    if (!Number.isSafeInteger(value) || value < 0) throw new CommandExecutionError(`Nowcoder returned a malformed frequencyData.${key}`);\n    return value;\n}\n\nfunction authorFields(userBrief, expectedId, label) {\n    if (!isRecord(userBrief)) throw new CommandExecutionError(`Nowcoder returned malformed ${label} authorship`);\n    const authorId = requiredId(userBrief.userId, `${label} userBrief.userId`);\n    if (authorId !== requiredId(expectedId, `${label} author id`)) throw new CommandExecutionError(`Nowcoder returned mismatched ${label} authorship`);\n    return {\n        author: optionalText(userBrief.nickname, `${label} author nickname`),\n        author_id: authorId,\n        author_url: `https://www.nowcoder.com/users/${authorId}`,\n        school: optionalText(userBrief.educationInfo, `${label} author education`),\n    };\n}\n\nfunction commonFeedFields(data, post, postType, authorId, timestamp, index) {\n    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'),","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/nowcoder/posts.js#L83-L119","documentation":"After extracting the author id from userBrief.userId, authorFields() cross-checks it against the id provided at the feed/detail level (contentData.authorId for content posts, momentData.userId for moments, or detail-level ids). This error means the two id sources disagree — the payload's author block belongs to a different user than the post's declared author, indicating inconsistent or tampered data.","triggerScenarios":"contentData.authorId ≠ data.userBrief.userId, or momentData.userId ≠ data.userBrief.userId, in a feed row; in projectNowcoderDetail, data.authorId/data.userId disagrees with data.userBrief.userId. Usually one of the two fields was renamed, defaulted, or points to a reposter/last-editor rather than the original author.","commonSituations":"Nowcoder changes which field carries the author id (e.g. authorId now holds an editor id); shared/reposted moments where userBrief reflects the sharer; partially refreshed cache returning stale userBrief with a new authorId; malformed mock/test fixtures.","solutions":["Log both ids (post-level authorId/userId and userBrief.userId) for the failing item and compare","If Nowcoder changed the semantics of one field, update the library to compare against the correct source","Test with a different post to determine whether it's item-specific corruption or an API-wide change","Clear any cached/proxied responses and re-fetch; stale caches can mix old and new author fields"],"exampleFix":"// before (payload changed: userId now the reposter)\n...authorFields(data.userBrief, post.userId, 'moment')\n// after (use the original-author field)\n...authorFields(data.userBrief, post.originalUserId ?? post.userId, 'moment')","handlingStrategy":"validation","validationCode":"function authorshipConsistent(data) {\n  const briefId = data?.userBrief?.userId;\n  const outerId = data?.contentData?.authorId ?? data?.momentData?.userId;\n  return String(briefId) === String(outerId);\n}\n// pre-check rows before projecting","typeGuard":"function hasMatchingAuthorIds(data) {\n  const a = data?.userBrief?.userId;\n  const b = data?.contentData?.authorId ?? data?.momentData?.userId;\n  return a != null && b != null && String(a) === String(b);\n}","tryCatchPattern":"try {\n  const rows = projectNowcoderFeed(records, limit, source);\n} catch (err) {\n  if (err instanceof CommandExecutionError && /mismatched .* authorship/.test(err.message)) {\n    // re-fetch the item fresh (cache may be stale) or flag for manual review\n  } else throw err;\n}","preventionTips":["Cross-check post-level author id against userBrief.userId in your own preprocessing","Re-fetch instead of trusting cached/mixed-version payloads","Re-verify field semantics (authorId vs userId vs sharer id) after API updates","Avoid constructing test fixtures with inconsistent author ids"],"tags":["nowcoder","api-schema","consistency","author"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}