{"record":{"id":"9b88dc670231e27a","repo":"jackwener/OpenCLI","slug":"webhost-feed-feed-item-is-missing-note-id","errorCode":null,"errorMessage":"${webHost} feed: feed item is missing note id","messagePattern":"(.+?) feed: feed item is missing note id","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/feed.js","lineNumber":122,"sourceCode":"    if (!data || typeof data !== 'object') {\n        throw new CommandExecutionError(`${webHost} feed: unexpected evaluate response`);\n    }\n    if (data.error) {\n        throw new CommandExecutionError(`${webHost} feed: ${data.error}`, `The SPA may still be hydrating; reload ${webHost}/explore and retry.`);\n    }\n    if (!Array.isArray(data.items)) {\n        throw new CommandExecutionError(`${webHost} feed: unexpected items payload shape`);\n    }\n    const rows = [];\n    for (const row of data.items) {\n        if (rows.length >= limit)\n            break;\n        if (!row || typeof row !== 'object') {\n            throw new CommandExecutionError(`${webHost} feed: malformed feed item`);\n        }\n        const id = toCleanString(row.id);\n        if (!id) {\n            throw new CommandExecutionError(`${webHost} feed: feed item is missing note id`);\n        }\n        const xsecToken = toCleanString(row.xsecToken);\n        if (!xsecToken) {\n            throw new CommandExecutionError(`${webHost} feed: feed item ${id} is missing xsecToken; cannot build a signed drill-down URL`);\n        }\n        rows.push({\n            id,\n            title: toCleanString(row.title),\n            type: toCleanString(row.type),\n            author: toCleanString(row.author),\n            likes: toCleanString(row.likes),\n            url: buildFeedNoteUrl(webHost, id, xsecToken),\n        });\n    }\n    if (rows.length === 0) {\n        throw new EmptyResultError(`${webHost}/feed`, 'No feed items in the hydrated store.');\n    }\n    return rows;","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/feed.js#L104-L140","documentation":"Every feed item must carry a note id; runFeed throws 'feed item is missing note id' when toCleanString(row.id) is empty. Without the id, drill-down URLs cannot be built, so the item is rejected.","triggerScenarios":"A row in data.items lacks an id field or has an id that trims to empty string (e.g. empty object, id renamed to noteId/idStr by a site change).","commonSituations":"Site schema rename (id -> noteId), ad/promoted cards without ids in the feed, skeleton items rendered before data binding.","solutions":["Inspect row keys in DevTools to find where the note id now lives","Update FEEDS_READ_JS or row mapping to read the renamed field (e.g. row.noteId)","Skip items without ids instead of aborting the whole feed","Retry after hydration so skeleton items resolve to real ids"],"exampleFix":"// before\nconst id = toCleanString(row.id);\n// after (tolerate renames)\nconst id = toCleanString(row.id ?? row.noteId ?? row.idStr);","handlingStrategy":"validation","validationCode":"const items = (data.items ?? []).filter(r => r && toCleanString(r.id ?? r.noteId ?? r.idStr));","typeGuard":"function hasNoteId(row) { return !!row && typeof row === 'object' && Boolean(toCleanString(row.id ?? row.noteId ?? row.idStr)); }","tryCatchPattern":"try {\n  const rows = await runFeed(page, webHost, limit);\n} catch (err) {\n  if (err instanceof CommandExecutionError && err.message.includes('missing note id')) {\n    // skip the item or reload and retry once hydrated\n  } else throw err;\n}","preventionTips":["Map all plausible id field names (id/noteId/idStr) in the row parser","Wait for hydration so skeleton items resolve to real ids","Filter id-less ad/placeholder cards before parsing"],"tags":["feed","schema","data-quality"],"backgroundTag":"missing-required-field","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}