jackwener/OpenCLI · error · CommandExecutionError

LinkedIn people search found profile candidates but could no

Error message

LinkedIn people search found profile candidates but could not parse stable result rows

What it means

This CommandExecutionError is thrown when a LinkedIn people search scrape found evidence of profile candidates (candidate_count or resolved_count > 0) but normalizePeopleRows could not extract any stable result rows from the DOM. It signals a parsing/DOM-structure failure rather than an empty result set: data exists but the scraper cannot read it.

Source

Thrown at clis/linkedin/people-search.js:238

            throw new CommandExecutionError(`LinkedIn people search extraction failed: ${error?.message || error}`);
        }
        if (result?.error) {
            if (looksLinkedInAuthWall(`${result.url || ''} ${result.error || ''}`)) {
                throw new AuthRequiredError(LINKEDIN_DOMAIN, 'LinkedIn people search requires an active signed-in browser session.');
            }
            // If LinkedIn redirected away from the search page that
            // usually means CUL was reached or the account is gated.
            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.`);
        }
        if (!result || typeof result !== 'object') {
            throw new CommandExecutionError('LinkedIn people search returned malformed extraction payload');
        }
        const candidateCount = parseNonNegativeCount(result.candidate_count, 'candidate_count');
        parseNonNegativeCount(result.person_entries_count, 'person_entries_count');
        const resolvedCount = parseNonNegativeCount(result.resolved_count, 'resolved_count');
        const rows = normalizePeopleRows(result.rows);
        if (rows.length === 0 && (candidateCount > 0 || resolvedCount > 0)) {
            throw new CommandExecutionError('LinkedIn people search found profile candidates but could not parse stable result rows');
        }
        if (rows.length === 0) {
            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.`);
        }
        return rows.slice(0, limit).map((p, i) => ({ rank: i + 1, ...p }));
    },
});

export const __test__ = {
    parseLimit,
    buildSearchUrl,
    normalizeProfileUrl,
    normalizePeopleRows,
    parseNonNegativeCount,
    extractionScript,
};

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Update the library to the latest version so normalizePeopleRows matches current LinkedIn markup
  2. Re-run the search after confirming you are signed in and the page renders the normal results layout
  3. Inspect the rendered page HTML and adjust the row selectors used by normalizePeopleRows
  4. Retry later or via a different account/region if LinkedIn is serving an experimental layout

Example fix

// before (outdated selector)
const rows = document.querySelectorAll('div.search-results__list > li');
// after (updated markup)
const rows = document.querySelectorAll('ul[role="list"] li.reusable-search__result-container');
Defensive patterns

Strategy: validation

Validate before calling

const result = await runSearch();
if (!Array.isArray(result?.rows) || result.rows.length === 0) {
  if ((result?.candidate_count ?? 0) > 0 || (result?.resolved_count ?? 0) > 0) {
    throw new Error('DOM parse failure: candidates exist but rows empty');
  }
}

Type guard

function hasParseableRows(result) {
  return Array.isArray(result?.rows) && result.rows.length > 0;
}

Try / catch

try {
  const rows = await linkedinPeopleSearch(keywords);
} catch (e) {
  if (String(e.message).includes('could not parse stable result rows')) {
    logger.warn('LinkedIn DOM changed; update scraper selectors', { keywords });
    throw e;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the people-search command when the rendered LinkedIn page reports positive candidate_count or resolved_count but result.rows normalizes to an empty array — typically because LinkedIn changed its people-search result markup or the selectors in normalizePeopleRows no longer match.

Common situations: LinkedIn ships a frontend redesign or A/B test with new CSS classes; scraping a locale/layout variant with different row markup; stale library version after a LinkedIn DOM update; headless page rendered in a fallback layout (e.g. auth wall or interstitial) that still reports counters.

Related errors


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