jackwener/OpenCLI · error · CommandExecutionError

LinkedIn jobs preferences returned malformed preferences pay

Error message

LinkedIn jobs preferences returned malformed preferences payload

What it means

Thrown by normalizePreferences when the preferences payload scraped from LinkedIn's job preferences page is not a non-null object. The extraction script returns whatever the DOM evaluate produced, and this guard rejects null/undefined/non-object results before field normalization. It means the page's structured data could not be extracted in the expected shape.

Source

Thrown at clis/linkedin/jobs-preferences.js:65

    const lines = text.split(/\n+/).map(clean).filter(Boolean);
    const alerts = [];
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      if (/alert/i.test(line) && line.length < 160) {
        alerts.push([line, lines[i + 1], lines[i + 2]].filter(Boolean).join(' | '));
      }
    }
    return {
      alerts_url: location.href,
      job_alerts: Array.from(new Set(alerts)).slice(0, 20),
      raw_preferences: clean(text).slice(0, 3000),
    };
  })()`;
}

function normalizePreferences(preferences, alerts) {
  if (!preferences || typeof preferences !== 'object') {
    throw new CommandExecutionError('LinkedIn jobs preferences returned malformed preferences payload');
  }
  if (!alerts || typeof alerts !== 'object') {
    throw new CommandExecutionError('LinkedIn jobs preferences returned malformed alerts payload');
  }
  const preferenceText = normalizeWhitespace(preferences.raw_preferences);
  const alertText = normalizeWhitespace(alerts.raw_preferences);
  if (!preferenceText && !alertText) {
    throw new CommandExecutionError('LinkedIn jobs preferences could not find stable preferences content');
  }
  return {
    open_to_work: inferOpenToWork(`${preferenceText} ${alertText}`),
    job_titles: Array.isArray(preferences.job_titles) ? preferences.job_titles.map(normalizeWhitespace).filter(Boolean).join('; ') : '',
    locations: Array.isArray(preferences.locations) ? preferences.locations.map(normalizeWhitespace).filter(Boolean).join('; ') : '',
    job_alerts: Array.isArray(alerts.job_alerts) ? alerts.job_alerts.map(normalizeWhitespace).filter(Boolean).join('; ') : '',
    preferences_url: normalizeWhitespace(preferences.preferences_url),
    alerts_url: normalizeWhitespace(alerts.alerts_url),
    raw_preferences: preferenceText.slice(0, 1200),
  };

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-authenticate (refresh valid LinkedIn cookies) so the real preferences page loads
  2. Increase the wait time after page.goto(PREFERENCES_URL) so dynamic content renders
  3. Inspect the raw evaluate output and update buildPreferencesScript() to match current LinkedIn DOM/JSON structure
  4. Log the payload before normalizePreferences to confirm whether authwall or markup change caused it

Example fix

// before
const preferences = unwrapEvaluateResult(await page.evaluate(buildPreferencesScript()));
return out(normalizePreferences(preferences, alerts));
// after
const preferences = unwrapEvaluateResult(await page.evaluate(buildPreferencesScript()));
if (!preferences || typeof preferences !== 'object') {
  await page.screenshot({ path: 'preferences-debug.png' });
}
return out(normalizePreferences(preferences, alerts));
Defensive patterns

Strategy: type-guard

Validate before calling

const prefs = unwrapEvaluateResult(await page.evaluate(buildPreferencesScript()));
if (!prefs || typeof prefs !== 'object' || Array.isArray(prefs)) {
  await page.screenshot({ path: 'prefs-debug.png' });
  throw new Error('preferences extraction returned non-object — likely authwall or DOM change');
}

Type guard

function isPreferencesPayload(v) {
  return typeof v === 'object' && v !== null && !Array.isArray(v) && ('raw_preferences' in v || 'job_titles' in v);
}

Try / catch

try {
  return normalizePreferences(preferences, alerts);
} catch (err) {
  if (String(err.message).includes('malformed preferences payload')) {
    await assertLinkedInAuthenticated(page, 'preferences');
    // retry extraction once after re-auth / longer wait
  } else throw err;
}

Prevention

When it happens

Trigger: page.evaluate(buildPreferencesScript()) returning null, undefined, a string, or an array because the preferences page DOM/JSON blob was absent, or unwrapEvaluateResult returning a non-object.

Common situations: LinkedIn served a login/authwall or CAPTCHA page instead of preferences so the script found no data; LinkedIn changed its preferences page markup breaking the extraction script; page.wait(5) was too short and data hadn't rendered.

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/fddf6c3486e2f42b. Report an issue: GitHub.