jackwener/OpenCLI · error · CommandExecutionError

LinkedIn Learning searchV2 returned malformed payload: missi

Error message

LinkedIn Learning searchV2 returned malformed payload: missing elements array

What it means

Thrown when the searchV2 API responds with valid JSON but that JSON lacks an 'elements' array at the top level. The CLI expects a RestLi-style collection payload; anything else (an error envelope, an HTML/JSON hybrid, or a changed schema) is treated as malformed. This guards the row-parsing loop from crashing on unexpected shapes.

Source

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

    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;
    },
});

export const __test__ = {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log result.json keys to inspect the actual payload shape and confirm schema drift.
  2. Update the CLI to the latest version that matches the current LinkedIn searchV2 schema.
  3. Add a fallback to parse alternate payload shapes (e.g. result.json.data?.elements or included arrays).
  4. Check LinkedIn Learning in the browser to see if search still works (indicates an API-side change, not local config).

Example fix

// before
const elements = result.json?.elements;
if (!Array.isArray(elements)) throw new CommandExecutionError('...missing elements array');
// after
const raw = result.json?.elements ?? result.json?.data?.elements;
if (!Array.isArray(raw)) throw new CommandExecutionError('...missing elements array: keys=' + Object.keys(result.json ?? {}).join(','));
Defensive patterns

Strategy: type-guard

Validate before calling

// after fetch, validate expected RestLi shape before consuming
if (!result?.json || !Array.isArray(result.json.elements)) {
  throw new Error('Unexpected searchV2 payload: ' + JSON.stringify(result?.json)?.slice(0, 200));
}

Type guard

function isSearchV2Payload(json) {
  return !!json && typeof json === 'object' && Array.isArray(json.elements) &&
    json.elements.every(el => el && typeof el === 'object');
}

Try / catch

try {
  const rows = await search(page, { keywords });
} catch (e) {
  if (e.message.includes('malformed payload')) {
    console.error('LinkedIn schema drift detected; update the CLI or inspect payload', e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: result.json exists but result.json.elements is not an Array — e.g. the endpoint returned {'status':404,'message':...} style RestLi error JSON, an included/wrapper object, or LinkedIn changed the searchV2 response schema.

Common situations: LinkedIn deprecating or reshaping the internal searchV2 API (schema drift), the endpoint returning an error envelope with HTTP 200, or an A/B-tested variant payload without elements.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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