{"record":{"id":"86fff4d7e49b37d7","repo":"jackwener/OpenCLI","slug":"linkedin-learning-feedrecommendationgroups-returne","errorCode":null,"errorMessage":"LinkedIn Learning feedRecommendationGroups returned malformed payload: missing elements array","messagePattern":"LinkedIn Learning feedRecommendationGroups returned malformed payload: missing elements array","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin-learning/trending.js","lineNumber":50,"sourceCode":"    domain: DOMAIN,\n    strategy: Strategy.COOKIE,\n    browser: true,\n    args: [\n        { name: 'limit', type: 'int', default: 10, help: `Maximum results to return (1-${MAX_LIMIT})` },\n    ],\n    columns: ['rank', 'group', 'type', 'title', 'difficulty', 'viewers', 'url'],\n    func: async (page, args) => {\n        if (!page) throw new CommandExecutionError('Browser session required for linkedin-learning trending');\n        const limit = parseLimit(args.limit);\n\n        const url = `https://www.linkedin.com/learning-api/feedRecommendationGroups?countPerCarousel=${MAX_PER_CAROUSEL}&q=learner`;\n        const result = await fetchLinkedInLearningApi(page, url);\n        if (!result?.json) {\n            throw new CommandExecutionError(`LinkedIn Learning feedRecommendationGroups failed: ${result?.error ?? 'no payload'}`);\n        }\n        const groups = result.json?.elements;\n        if (!Array.isArray(groups)) {\n            throw new CommandExecutionError('LinkedIn Learning feedRecommendationGroups returned malformed payload: missing elements array');\n        }\n        const rows = [];\n        const seen = new Set();\n        let rank = 1;\n        let sawCards = false;\n        for (const group of groups) {\n            const carousels = Array.isArray(group?.carousels) ? group.carousels : [];\n            for (const carousel of carousels) {\n                const cards = Array.isArray(carousel?.cards) ? carousel.cards : [];\n                for (const card of cards) {\n                    sawCards = true;\n                    if (rows.length >= limit) break;\n                    const slug = card?.slug;\n                    if (!slug || seen.has(slug)) continue;\n                    seen.add(slug);\n                    const row = parseCard(card, carousel, rank);\n                    if (!row) continue;\n                    rows.push(row);","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin-learning/trending.js#L32-L68","documentation":"Thrown when feedRecommendationGroups returns JSON that does not contain a top-level 'elements' array. The trending logic iterates groups from result.json.elements; any other shape (RestLi error envelope, wrapper object, changed schema) is rejected as malformed before parsing to avoid undefined iteration.","triggerScenarios":"result.json exists but result.json.elements is not an Array — e.g. a RestLi error object with HTTP 200, a payload wrapped in data/included keys, or LinkedIn changing the feedRecommendationGroups response schema.","commonSituations":"LinkedIn A/B-testing a new feed payload shape, endpoint deprecation/schema drift breaking an older CLI version, or an error envelope returned with a 200 status.","solutions":["Log Object.keys(result.json) to see the actual payload structure.","Update the CLI to a version matching the current feedRecommendationGroups schema.","Add a fallback to unwrap alternate shapes (result.json.data?.elements or included arrays).","Verify trending carousels still render on the LinkedIn Learning web page (indicates an API change, not local state)."],"exampleFix":"// before\nconst groups = result.json?.elements;\n// after\nconst groups = result.json?.elements ?? result.json?.data?.elements;\nif (!Array.isArray(groups)) throw new CommandExecutionError('malformed payload: keys=' + Object.keys(result.json ?? {}).join(','));","handlingStrategy":"type-guard","validationCode":"// validate the groups payload before iterating\nconst json = result?.json;\nif (!json || !Array.isArray(json.elements)) {\n  throw new Error('Unexpected feedRecommendationGroups payload: ' + JSON.stringify(json)?.slice(0, 200));\n}","typeGuard":"function isFeedGroupsPayload(json) {\n  return !!json && typeof json === 'object' && Array.isArray(json.elements) &&\n    json.elements.every(g => g && typeof g === 'object');\n}","tryCatchPattern":"try {\n  const rows = await trending(page);\n} catch (e) {\n  if (e.message.includes('malformed payload')) {\n    console.error('feedRecommendationGroups schema changed; inspect payload keys and update CLI');\n  } else throw e;\n}","preventionTips":["Keep the CLI updated against LinkedIn feed API schema changes","Log payload top-level keys when the guard fires to diagnose drift","Support fallback unwrapping (data.elements / included) in custom parsers","Correlate failures with LinkedIn web UI behavior to distinguish API changes from local issues"],"tags":["schema","api","linkedin","payload"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}