{"record":{"id":"9f3618145558d1b7","repo":"jackwener/OpenCLI","slug":"linkedin-people-search-returned-malformed-extracti-9f3618","errorCode":null,"errorMessage":"LinkedIn people search returned malformed extraction payload","messagePattern":"LinkedIn people search returned malformed extraction payload","errorType":"error_code","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/people-search.js","lineNumber":231,"sourceCode":"            throw new AuthRequiredError(LINKEDIN_DOMAIN, 'LinkedIn JSESSIONID cookie not found. Please sign in to LinkedIn in the browser.');\n        }\n\n        let result;\n        try {\n            result = unwrapEvaluateResult(await page.evaluate(extractionScript()));\n        } catch (error) {\n            throw new CommandExecutionError(`LinkedIn people search extraction failed: ${error?.message || error}`);\n        }\n        if (result?.error) {\n            if (looksLinkedInAuthWall(`${result.url || ''} ${result.error || ''}`)) {\n                throw new AuthRequiredError(LINKEDIN_DOMAIN, 'LinkedIn people search requires an active signed-in browser session.');\n            }\n            // If LinkedIn redirected away from the search page that\n            // usually means CUL was reached or the account is gated.\n            throw new CommandExecutionError(`LinkedIn redirected away from the search page (${result.error}). Likely Commercial Use Limit reached - the limit resets on the 1st of next month.`);\n        }\n        if (!result || typeof result !== 'object') {\n            throw new CommandExecutionError('LinkedIn people search returned malformed extraction payload');\n        }\n        const candidateCount = parseNonNegativeCount(result.candidate_count, 'candidate_count');\n        parseNonNegativeCount(result.person_entries_count, 'person_entries_count');\n        const resolvedCount = parseNonNegativeCount(result.resolved_count, 'resolved_count');\n        const rows = normalizePeopleRows(result.rows);\n        if (rows.length === 0 && (candidateCount > 0 || resolvedCount > 0)) {\n            throw new CommandExecutionError('LinkedIn people search found profile candidates but could not parse stable result rows');\n        }\n        if (rows.length === 0) {\n            throw new EmptyResultError(`No people found on the rendered page for \"${keywords}\". The search may have returned zero results, or the DOM markup may have changed.`);\n        }\n        return rows.slice(0, limit).map((p, i) => ({ rank: i + 1, ...p }));\n    },\n});\n\nexport const __test__ = {\n    parseLimit,\n    buildSearchUrl,","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/people-search.js#L213-L249","documentation":"After a successful evaluate, the command requires result to be a non-null object before validating its numeric fields. If result is null/undefined or a primitive, the extraction produced nothing usable and it throws CommandExecutionError 'LinkedIn people search returned malformed extraction payload'.","triggerScenarios":"unwrapEvaluateResult(page.evaluate(extractionScript())) resolves to null, undefined, a string, or a number — e.g. the script returned undefined, the evaluate wrapper stripped the value, or serialization dropped the object.","commonSituations":"Custom automation client whose evaluate returns undefined for object results; extraction script exiting early and returning undefined; test stubs returning wrong shape; JSON serialization of circular/unserializable data collapsing the result.","solutions":["Update/fix the automation client so page.evaluate returns the object produced by the script","Ensure unwrapEvaluateResult is used/behaves as intended and doesn't drop object payloads","Log the raw evaluate result to see what the page actually returned","Re-run in a signed-in session so the real results page produces a full payload","If you own the script, guarantee every code path returns the result object"],"exampleFix":"// before: script path returns undefined\nfunction extractionScript() { return `() => { if (!document.querySelector('.results')) return; return {...}; }`; }\n// after: always return a payload\nfunction extractionScript() { return `() => ({ error: null, rows: [], candidate_count: 0, person_entries_count: 0, resolved_count: 0, ...collect() })`; }","handlingStrategy":"type-guard","validationCode":"const raw = unwrapEvaluateResult(await page.evaluate(extractionScript()));\nif (raw == null || typeof raw !== 'object') throw new Error('extraction returned no object payload');","typeGuard":"const isExtractionPayload = (v) => v != null && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"try { const rows = normalizePeopleRows(result.rows); }\ncatch (e) { if (/malformed extraction payload/.test(e.message)) { return { rows: [], degraded: true }; } throw e; }","preventionTips":["Guarantee the extraction script always returns an object on every path","Verify your evaluate wrapper preserves object return values","Test page adapters/mocks against the real payload contract","Log raw results when payload shape deviates to catch adapter regressions early"],"tags":["linkedin","payload-validation","serialization","dom-parsing"],"backgroundTag":"malformed-extraction-payload","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}