jackwener/OpenCLI · error · CommandExecutionError

LinkedIn people search returned malformed extraction payload

Error message

LinkedIn people search returned malformed extraction payload: invalid ${label}

What it means

parseNonNegativeCount validates numeric extraction-payload fields (candidate_count, person_entries_count, resolved_count) returned by the injected LinkedIn DOM extraction script. If a value is not an integer >= 0, the library assumes the page-side extractor ran in a changed/broken DOM and throws CommandExecutionError naming the bad field via ${label}. This guards downstream row-normalization logic from garbage counts.

Source

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

        }
        const name = normalizeWhitespace(row.name);
        const profileUrl = normalizeProfileUrl(row.profile_url);
        if (!name || !profileUrl) {
            throw new CommandExecutionError(`LinkedIn people search returned row without stable profile identity at index ${index}`);
        }
        return {
            name,
            headline: normalizeWhitespace(row.headline),
            location: normalizeWhitespace(row.location),
            profile_url: profileUrl,
        };
    });
}

function parseNonNegativeCount(value, label) {
    const count = Number(value);
    if (!Number.isInteger(count) || count < 0) {
        throw new CommandExecutionError(`LinkedIn people search returned malformed extraction payload: invalid ${label}`);
    }
    return count;
}

function extractionScript() {
    // Class-based selectors are dead (LinkedIn rotates hashed class
    // names on every deploy) and display:contents flattens the DOM
    // tree so per-card containers don't exist. Read main.innerText
    // and slice between consecutive person-name lines instead.
    return String.raw`(() => {
    if (!/search\/results\/people/.test(window.location.href)) {
      return { error: 'not on people search page', url: window.location.href };
    }
    const main = document.querySelector('main') || document.body;
    const normalize = (s) => String(s || '').replace(/[\s\u00a0\u202f]+/g, ' ').trim();
    const collapseRepeatedName = (name) => {
      const parts = normalize(name).split(' ').filter(Boolean);
      if (parts.length === 0 || parts.length % 2 !== 0) return normalize(name);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Update the package to the latest version that matches current LinkedIn markup
  2. Re-run the search in a signed-in browser session so the real results page (with counters) renders
  3. Log result.candidate_count/result.person_entries_count/result.resolved_count in the evaluate callback to see which field is malformed and why
  4. Check whether LinkedIn redirected to an auth/CUL page and clear that first (see errors 2345/2348)
  5. File/inspect the extractionScript() selectors and patch them for the new LinkedIn layout

Example fix

// before: payload field missing, throws
const candidateCount = parseNonNegativeCount(result.candidate_count, 'candidate_count');
// after: default missing fields to 0 before validation
const candidateCount = parseNonNegativeCount(result.candidate_count ?? 0, 'candidate_count');
Defensive patterns

Strategy: validation

Validate before calling

function isValidCount(v) { return Number.isInteger(Number(v)) && Number(v) >= 0; }
const payload = result ?? {};
if (![payload.candidate_count, payload.person_entries_count, payload.resolved_count].every(isValidCount)) {
  throw new Error('extraction payload has invalid count fields');
}

Type guard

const isExtractionCounts = (o) => o != null && typeof o === 'object' &&
  ['candidate_count', 'person_entries_count', 'resolved_count'].every((k) => Number.isInteger(Number(o[k])) && Number(o[k]) >= 0);

Try / catch

try { const count = parseNonNegativeCount(result.candidate_count, 'candidate_count'); } catch (e) { logger.warn('malformed counts', result); /* fallback: proceed with rows only */ }

Prevention

When it happens

Trigger: Calling the linkedin people-search command when page.evaluate(extractionScript()) returns a result object whose candidate_count, person_entries_count, or resolved_count is undefined, a non-integer number, negative, or a non-numeric string — e.g. LinkedIn changed the page markup so the counter selectors resolve to nothing.

Common situations: LinkedIn DOM/markup updates breaking the in-page extractor; scraping an empty/interstitial results page; LinkedIn serving a variant layout (different locale or mobile layout); a partial extraction script that sets counts conditionally.

Understand the failure class

Related errors


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