{"record":{"id":"783c74e94fed6427","repo":"jackwener/OpenCLI","slug":"nowcoder-detail-returned-malformed-post-data","errorCode":null,"errorMessage":"Nowcoder detail returned malformed post data","messagePattern":"Nowcoder detail returned malformed post data","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/nowcoder/posts.js","lineNumber":202,"sourceCode":"        url = new URL(value);\n    }\n    catch {\n        throw new ArgumentError('nowcoder detail requires a numeric content ID, moment UUID, or canonical Nowcoder URL');\n    }\n    const host = url.hostname.toLowerCase();\n    if (url.protocol !== 'https:' || url.username || url.password || url.port || url.hash\n        || (host !== 'nowcoder.com' && host !== 'www.nowcoder.com')) {\n        throw new ArgumentError('nowcoder detail only accepts canonical https://www.nowcoder.com post URLs');\n    }\n    const content = url.pathname.match(/^\\/discuss\\/([1-9]\\d*)\\/?$/);\n    if (content) return { post_type: 'content', value: content[1] };\n    const moment = url.pathname.match(/^\\/feed\\/main\\/detail\\/([0-9a-f]{32})\\/?$/i);\n    if (moment) return { post_type: 'moment', value: moment[1].toLowerCase() };\n    throw new ArgumentError('Unsupported Nowcoder URL; expected /discuss/<content-id> or /feed/main/detail/<moment-uuid>');\n}\n\nexport function projectNowcoderDetail(data, target) {\n    if (!isRecord(data) || !isRecord(data.frequencyData)) throw new CommandExecutionError('Nowcoder detail returned malformed post data');\n    const isContent = target.post_type === 'content';\n    const expectedEntityType = isContent ? CONTENT_ENTITY_TYPE : MOMENT_TYPE;\n    if (data.entityType !== expectedEntityType) throw new CommandExecutionError('Nowcoder detail returned a mismatched post entity type');\n    const uuid = requiredUuid(data.uuid, `${target.post_type} uuid`);\n    const entityId = requiredId(data.entityId, `${target.post_type} entity id`);\n    const id = isContent ? requiredId(data.id, 'content id') : uuid;\n    if (isContent ? id !== target.value : uuid !== target.value) throw new CommandExecutionError('Nowcoder detail returned a different post identity');\n    if (!isContent && entityId !== requiredId(data.id, 'moment id')) throw new CommandExecutionError('Nowcoder detail returned mismatched moment entity ids');\n    const expectedAuthorId = isContent ? data.authorId : data.userId;\n    const body = cleanBody(isContent ? data.richText : data.content, `${target.post_type} detail body`);\n    return {\n        post_type: target.post_type,\n        id,\n        uuid,\n        entity_id: entityId,\n        url: isContent\n            ? `https://www.nowcoder.com/discuss/${id}`\n            : `https://www.nowcoder.com/feed/main/detail/${uuid}`,","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/nowcoder/posts.js#L184-L220","documentation":"projectNowcoderDetail validates the JSON returned by the Nowcoder detail API before projecting it into the CLI output. It requires the payload to be an object containing a frequencyData object (the engagement-metrics sub-object). If the API returns null, an array, or an object without frequencyData, the library considers the response malformed and throws this CommandExecutionError rather than producing partially-filled output.","triggerScenarios":"The detail endpoint responded with a JSON body that is not a record or lacks the frequencyData field — e.g. the post was deleted or made private and the API returned an empty/null data payload, the endpoint changed shape, or an error page was returned with HTTP 200.","commonSituations":"Viewing a recently deleted or hidden post; a post that requires login to view; Nowcoder changing its internal API response schema; hitting a different regional endpoint or anti-bot interstitial that returns HTML/empty JSON.","solutions":["Verify the post still exists and is publicly viewable in a browser while logged in.","Log in to Nowcoder (the CLI uses your browser session) and retry — restricted posts may return partial data.","Retry later in case of transient API issues; if persistent, report the schema drift since the library may need updating.","Re-check that the correct content ID / moment UUID was passed, since wrong IDs can resolve to empty payloads."],"exampleFix":"// before\nnowcoder detail 99999999999  // deleted/nonexistent post -> malformed data\n// after\nnowcoder detail 70123456  // verify the post loads at nowcoder.com/discuss/70123456 first","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function looksLikeNowcoderDetail(data) {\n  return data !== null && typeof data === 'object' && !Array.isArray(data)\n    && typeof data.frequencyData === 'object' && data.frequencyData !== null;\n}","tryCatchPattern":"try {\n  const post = await nowcoderDetail(id);\n} catch (err) {\n  if (err instanceof CommandExecutionError && /malformed post data/.test(err.message)) {\n    console.error('Post may be deleted, private, or schema-drifted; verify it in a browser:', id);\n  } else throw err;\n}","preventionTips":["Confirm the post loads in a logged-in browser before scripting against it.","Re-verify IDs/UUIDs after posts are deleted or merged.","Keep the CLI/library updated for Nowcoder API schema changes.","Treat repeated malformed-data errors for the same ID as 'post gone' rather than retrying forever."],"tags":["api-response","schema-validation","network"],"backgroundTag":"malformed-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}