{"record":{"id":"04900d9a72e6dc07","repo":"jackwener/OpenCLI","slug":"linkedin-learning-searchv2-failed-result-error","errorCode":null,"errorMessage":"LinkedIn Learning searchV2 failed: ${result?.error ?? 'no payload'}","messagePattern":"LinkedIn Learning searchV2 failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin-learning/search.js","lineNumber":72,"sourceCode":"    description: 'Search LinkedIn Learning courses, videos, and learning paths by keyword',\n    domain: DOMAIN,\n    strategy: Strategy.COOKIE,\n    browser: true,\n    args: [\n        { name: 'keywords', type: 'string', required: true, positional: true, help: 'Search keywords, e.g. \"AI agent\"' },\n        { name: 'limit', type: 'int', default: 10, help: `Maximum results to return (1-${MAX_LIMIT})` },\n    ],\n    columns: ['rank', 'type', 'title', 'instructor', 'difficulty', 'duration_sec', 'rating', 'rating_count', 'viewers', 'url'],\n    func: async (page, args) => {\n        if (!page) throw new CommandExecutionError('Browser session required for linkedin-learning search');\n        const keywords = normalizeWhitespace(args.keywords);\n        if (!keywords) throw new ArgumentError('--keywords is required');\n        const limit = parseLimit(args.limit);\n\n        const url = `https://www.linkedin.com/learning-api/searchV2?keywords=${encodeURIComponent(keywords)}&q=keywords`;\n        const result = await fetchLinkedInLearningApi(page, url);\n        if (!result?.json) {\n            throw new CommandExecutionError(`LinkedIn Learning searchV2 failed: ${result?.error ?? 'no payload'}`);\n        }\n        const elements = result.json?.elements;\n        if (!Array.isArray(elements)) {\n            throw new CommandExecutionError('LinkedIn Learning searchV2 returned malformed payload: missing elements array');\n        }\n        if (elements.length === 0) {\n            throw new EmptyResultError(`No LinkedIn Learning results for \"${keywords}\"`);\n        }\n        const rows = [];\n        for (const el of elements) {\n            if (rows.length >= limit) break;\n            const row = parseRow(el, rows.length + 1);\n            if (row) rows.push(row);\n        }\n        if (rows.length === 0) {\n            throw new CommandExecutionError('LinkedIn Learning searchV2 returned no parseable rows with slug identity');\n        }\n        return rows;","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin-learning/search.js#L54-L90","documentation":"Thrown by the linkedin-learning search command when the fetch of the internal LinkedIn searchV2 API completes but returns no JSON payload (result.json is absent). The in-page fetch script reports the failure reason in result.error (e.g. 'HTTP 500' or 'fetch failed: ...'); when even that is missing the message falls back to 'no payload'. It signals the API call itself did not yield usable data.","triggerScenarios":"The in-page fetch throws (network/CORS/aborted) and returns {error:'fetch failed: ...'}, or the server responds non-OK with a status that is not 401/403 (e.g. HTTP 429 rate limit or HTTP 500), so buildFetchScript returns {error:'HTTP <status>'} instead of {json}.","commonSituations":"LinkedIn rate-limiting or throttling the signed-in session, transient LinkedIn Learning API outages (5xx), the session being soft-logged-out so the API returns a non-401/403 redirect/HTML status, or the page being navigated away mid-fetch.","solutions":["Re-run the command after a short delay; transient 5xx/429 responses are the most common cause.","Re-authenticate: open the browser session and confirm you are signed in to LinkedIn Learning, then retry.","Check the interpolated result.error in the full message for the HTTP status and act on it (429 = back off, 5xx = retry later).","Verify network connectivity / proxy settings for the automated browser.","Update the CLI: LinkedIn may have changed the searchV2 endpoint contract."],"exampleFix":"// before\nconst result = await fetchLinkedInLearningApi(page, url);\nif (!result?.json) throw new CommandExecutionError(`searchV2 failed: ${result?.error ?? 'no payload'}`);\n// after\nconst result = await fetchLinkedInLearningApi(page, url);\nif (!result?.json) {\n  if (result?.error?.startsWith('HTTP 429')) await sleep(30000);\n  throw new CommandExecutionError(`searchV2 failed: ${result?.error ?? 'no payload'}`);\n}","handlingStrategy":"try-catch","validationCode":"// pre-check session before calling search\nconst cookies = await page.getCookies({ url: 'https://www.linkedin.com' });\nif (!cookies.some(c => c.name === 'JSESSIONID')) throw new Error('Sign in to LinkedIn first');","typeGuard":"function hasJson(r) { return !!r && typeof r === 'object' && r.json !== null && typeof r.json === 'object'; }","tryCatchPattern":"try {\n  const rows = await search(page, { keywords, limit });\n} catch (e) {\n  if (/HTTP 429/.test(e.message)) { await sleep(30000); /* retry once */ }\n  else if (/auth/i.test(e.message)) { promptRelogin(); }\n  else throw e;\n}","preventionTips":["Keep the browser profile signed in to LinkedIn before running search commands","Back off and retry on 429/5xx statuses surfaced in result.error","Watch result.error in messages for early signs of session or rate-limit trouble","Retry transient network failures with exponential backoff"],"tags":["network","api","http","linkedin"],"backgroundTag":"upstream-api-non-ok-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}