jackwener/OpenCLI · error · CommandExecutionError

LinkedIn Learning searchV2 failed: ${result?.error ?? 'no pa

Error message

LinkedIn Learning searchV2 failed: ${result?.error ?? 'no payload'}

What it means

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.

Source

Thrown at clis/linkedin-learning/search.js:72

    description: 'Search LinkedIn Learning courses, videos, and learning paths by keyword',
    domain: DOMAIN,
    strategy: Strategy.COOKIE,
    browser: true,
    args: [
        { name: 'keywords', type: 'string', required: true, positional: true, help: 'Search keywords, e.g. "AI agent"' },
        { name: 'limit', type: 'int', default: 10, help: `Maximum results to return (1-${MAX_LIMIT})` },
    ],
    columns: ['rank', 'type', 'title', 'instructor', 'difficulty', 'duration_sec', 'rating', 'rating_count', 'viewers', 'url'],
    func: async (page, args) => {
        if (!page) throw new CommandExecutionError('Browser session required for linkedin-learning search');
        const keywords = normalizeWhitespace(args.keywords);
        if (!keywords) throw new ArgumentError('--keywords is required');
        const limit = parseLimit(args.limit);

        const url = `https://www.linkedin.com/learning-api/searchV2?keywords=${encodeURIComponent(keywords)}&q=keywords`;
        const result = await fetchLinkedInLearningApi(page, url);
        if (!result?.json) {
            throw new CommandExecutionError(`LinkedIn Learning searchV2 failed: ${result?.error ?? 'no payload'}`);
        }
        const elements = result.json?.elements;
        if (!Array.isArray(elements)) {
            throw new CommandExecutionError('LinkedIn Learning searchV2 returned malformed payload: missing elements array');
        }
        if (elements.length === 0) {
            throw new EmptyResultError(`No LinkedIn Learning results for "${keywords}"`);
        }
        const rows = [];
        for (const el of elements) {
            if (rows.length >= limit) break;
            const row = parseRow(el, rows.length + 1);
            if (row) rows.push(row);
        }
        if (rows.length === 0) {
            throw new CommandExecutionError('LinkedIn Learning searchV2 returned no parseable rows with slug identity');
        }
        return rows;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command after a short delay; transient 5xx/429 responses are the most common cause.
  2. Re-authenticate: open the browser session and confirm you are signed in to LinkedIn Learning, then retry.
  3. Check the interpolated result.error in the full message for the HTTP status and act on it (429 = back off, 5xx = retry later).
  4. Verify network connectivity / proxy settings for the automated browser.
  5. Update the CLI: LinkedIn may have changed the searchV2 endpoint contract.

Example fix

// before
const result = await fetchLinkedInLearningApi(page, url);
if (!result?.json) throw new CommandExecutionError(`searchV2 failed: ${result?.error ?? 'no payload'}`);
// after
const result = await fetchLinkedInLearningApi(page, url);
if (!result?.json) {
  if (result?.error?.startsWith('HTTP 429')) await sleep(30000);
  throw new CommandExecutionError(`searchV2 failed: ${result?.error ?? 'no payload'}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check session before calling search
const cookies = await page.getCookies({ url: 'https://www.linkedin.com' });
if (!cookies.some(c => c.name === 'JSESSIONID')) throw new Error('Sign in to LinkedIn first');

Type guard

function hasJson(r) { return !!r && typeof r === 'object' && r.json !== null && typeof r.json === 'object'; }

Try / catch

try {
  const rows = await search(page, { keywords, limit });
} catch (e) {
  if (/HTTP 429/.test(e.message)) { await sleep(30000); /* retry once */ }
  else if (/auth/i.test(e.message)) { promptRelogin(); }
  else throw e;
}

Prevention

When it happens

Trigger: 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}.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/04900d9a72e6dc07. Report an issue: GitHub.