jackwener/OpenCLI · error · CommandExecutionError

LinkedIn company extraction returned a malformed payload

Error message

LinkedIn company extraction returned a malformed payload

What it means

normalizeCompanyInfo expects the extraction payload to be a plain object. If the in-page script returned null, undefined, an array, or any non-object, the library throws because it cannot read name/followers fields from it. This guards against silently producing an empty result when extraction failed structurally.

Source

Thrown at clis/linkedin/company.js:103

    if (parsed.protocol !== 'https:' || parsed.username || parsed.password || parsed.port || !LINKEDIN_COMPANY_HOSTS.has(parsed.hostname.toLowerCase())) {
        throw new CommandExecutionError('LinkedIn company extraction ended on a non-LinkedIn page');
    }
    const match = parsed.pathname.match(COMPANY_URL_RE);
    if (!match?.[1]) {
        throw new CommandExecutionError('LinkedIn company extraction ended outside a company page');
    }
    let slug;
    try {
        slug = decodeURIComponent(match[1]);
    } catch {
        throw new CommandExecutionError('LinkedIn company extraction returned a malformed company slug');
    }
    return `https://${LINKEDIN_DOMAIN}/company/${encodeURIComponent(slug)}/about/`;
}

function normalizeCompanyInfo(info, targetUrl) {
    if (!info || typeof info !== 'object' || Array.isArray(info)) {
        throw new CommandExecutionError('LinkedIn company extraction returned a malformed payload');
    }
    if (!info.name) {
        throw new CommandExecutionError('LinkedIn company page rendered but no company name was found (layout drift or company not found)');
    }
    let followers = 0;
    if (info.followers) {
        followers = Number(info.followers);
        if (!Number.isFinite(followers)) {
            throw new CommandExecutionError('LinkedIn company extraction returned a malformed followers count');
        }
    }
    return {
        name: String(info.name),
        industry: String(info.industry || ''),
        size: String(info.size || ''),
        headquarters: String(info.headquarters || ''),
        founded: String(info.founded || ''),
        website: String(info.website || ''),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Update the library to a version whose extraction script matches the current LinkedIn layout
  2. Confirm the session reached a real company page (not a login or captcha wall) before extraction
  3. Retry the command; transient rendering failures can yield empty payloads
  4. Inspect the raw extractor output by logging before normalization to see what shape was returned
Defensive patterns

Strategy: try-catch

Type guard

function isExtractionPayload(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }

Try / catch

try {
  const info = await company({ url: target });
} catch (err) {
  if (String(err.message).includes('malformed payload')) {
    // extraction returned non-object: check for captcha/login wall, retry once
    return withRetry(() => company({ url: target }), 1);
  }
  throw err;
}

Prevention

When it happens

Trigger: Page script returned null/undefined because its querySelectors matched nothing and it bailed out; serialization between the browser context and Node mangled the result into a string; extraction ran on a page whose layout changed so the payload builder returned an array or nothing.

Common situations: LinkedIn markup update (layout drift) breaking the extractor's selectors; scraping a captcha/login interstitial where no company data exists; version mismatch between the CLI and the injected extraction script.

Understand the failure class

Related errors


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