{"record":{"id":"08ba51d9defe673b","repo":"jackwener/OpenCLI","slug":"xiaohongshu-collection-api-returned-malformed-data","errorCode":null,"errorMessage":"xiaohongshu collection API returned malformed data","messagePattern":"xiaohongshu collection API returned malformed data","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/collection-helpers.js","lineNumber":95,"sourceCode":"        title: toCleanString(noteCard.display_title ?? noteCard.displayTitle ?? noteCard.title ?? entry.title ?? entry.display_title),\n        author: toCleanString(user.nickname ?? user.nick_name ?? user.name),\n        likes: toCleanString(interact.liked_count ?? interact.likedCount ?? 0) || '0',\n        type: toCleanString(noteCard.type ?? entry.type),\n        url,\n    };\n}\n\nexport function extractNotesFromResponses(requests, fallbackUserId) {\n    const rows = [];\n    const seen = new Set();\n    for (const req of requests ?? []) {\n        const payload = unwrapBrowserResult(req);\n        if (!isObject(payload)) {\n            throw new CommandExecutionError('xiaohongshu collection API returned a malformed payload');\n        }\n        const data = payload.data;\n        if (!isObject(data)) {\n            throw new CommandExecutionError('xiaohongshu collection API returned malformed data');\n        }\n        const notes = data.notes ?? data.note_list;\n        if (!Array.isArray(notes))\n            throw new CommandExecutionError('xiaohongshu collection API returned malformed notes');\n        for (const entry of notes) {\n            const row = mapCollectionNote(entry, { fallbackUserId });\n            if (!row?.id || !row.url.includes('xsec_token=')) {\n                throw new CommandExecutionError('xiaohongshu collection API returned a note without stable id/xsec token');\n            }\n            if (seen.has(row.id))\n                continue;\n            seen.add(row.id);\n            rows.push(row);\n        }\n    }\n    return rows;\n}\n","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/collection-helpers.js#L77-L113","documentation":"extractNotesFromResponses expects each collection API payload to carry its data under payload.data as an object. When the unwrapped payload is an object but data is missing or not an object (e.g. {success:false} or {data:null}), it throws CommandExecutionError. The API contract is that note lists live inside data.notes or data.note_list.","triggerScenarios":"The xiaohongshu collection endpoint returns an error envelope like {code:..., msg:'...', data:null}, or the API response schema changed so notes no longer sit under data.","commonSituations":"Rate-limited or partially failed API calls returning error envelopes; xiaohongshu changing their internal API response shape; hitting the endpoint without proper cookies causing a soft error object.","solutions":["Re-login / refresh session cookies and retry","Log the offending payload (JSON.stringify(payload)) to see the API error code","Slow down request rate to avoid API-level errors","Check for xiaohongshu API schema changes and update field mapping"],"exampleFix":"// before\nconst payload = JSON.parse(body); // payload = {code:-1, data:null}\nrows = extractNotesFromResponses([payload], userId); // throws\n// after\nif (payload && payload.data) {\n  rows = extractNotesFromResponses([payload], userId);\n} else {\n  console.error('API envelope:', JSON.stringify(payload));\n}","handlingStrategy":"type-guard","validationCode":"const hasDataObject = (payload) => payload && typeof payload === 'object' && payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data);","typeGuard":"const hasDataObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v) && v.data !== null && typeof v.data === 'object' && !Array.isArray(v.data);","tryCatchPattern":"try { rows = extractNotesFromResponses(reqs, userId); } catch (e) { if (String(e.message).includes('malformed data')) { console.warn('API envelope:', JSON.stringify(reqs)); rows = []; } else throw e; }","preventionTips":["Inspect API error envelopes (code/msg fields) and handle them as auth/rate-limit signals","Throttle requests to avoid error envelopes from rate limiting","Re-check field mapping after any xiaohongshu API change","Log the full payload on failure for diagnosis"],"tags":["scraping","malformed-response","api-schema","command-execution-error"],"backgroundTag":"malformed-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}