{"record":{"id":"fddf6c3486e2f42b","repo":"jackwener/OpenCLI","slug":"linkedin-jobs-preferences-returned-malformed-prefe","errorCode":null,"errorMessage":"LinkedIn jobs preferences returned malformed preferences payload","messagePattern":"LinkedIn jobs preferences returned malformed preferences payload","errorType":"error_code","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/jobs-preferences.js","lineNumber":65,"sourceCode":"    const lines = text.split(/\\n+/).map(clean).filter(Boolean);\n    const alerts = [];\n    for (let i = 0; i < lines.length; i++) {\n      const line = lines[i];\n      if (/alert/i.test(line) && line.length < 160) {\n        alerts.push([line, lines[i + 1], lines[i + 2]].filter(Boolean).join(' | '));\n      }\n    }\n    return {\n      alerts_url: location.href,\n      job_alerts: Array.from(new Set(alerts)).slice(0, 20),\n      raw_preferences: clean(text).slice(0, 3000),\n    };\n  })()`;\n}\n\nfunction normalizePreferences(preferences, alerts) {\n  if (!preferences || typeof preferences !== 'object') {\n    throw new CommandExecutionError('LinkedIn jobs preferences returned malformed preferences payload');\n  }\n  if (!alerts || typeof alerts !== 'object') {\n    throw new CommandExecutionError('LinkedIn jobs preferences returned malformed alerts payload');\n  }\n  const preferenceText = normalizeWhitespace(preferences.raw_preferences);\n  const alertText = normalizeWhitespace(alerts.raw_preferences);\n  if (!preferenceText && !alertText) {\n    throw new CommandExecutionError('LinkedIn jobs preferences could not find stable preferences content');\n  }\n  return {\n    open_to_work: inferOpenToWork(`${preferenceText} ${alertText}`),\n    job_titles: Array.isArray(preferences.job_titles) ? preferences.job_titles.map(normalizeWhitespace).filter(Boolean).join('; ') : '',\n    locations: Array.isArray(preferences.locations) ? preferences.locations.map(normalizeWhitespace).filter(Boolean).join('; ') : '',\n    job_alerts: Array.isArray(alerts.job_alerts) ? alerts.job_alerts.map(normalizeWhitespace).filter(Boolean).join('; ') : '',\n    preferences_url: normalizeWhitespace(preferences.preferences_url),\n    alerts_url: normalizeWhitespace(alerts.alerts_url),\n    raw_preferences: preferenceText.slice(0, 1200),\n  };","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/jobs-preferences.js#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-authenticate (refresh valid LinkedIn cookies) so the real preferences page loads","Increase the wait time after page.goto(PREFERENCES_URL) so dynamic content renders","Inspect the raw evaluate output and update buildPreferencesScript() to match current LinkedIn DOM/JSON structure","Log the payload before normalizePreferences to confirm whether authwall or markup change caused it"],"exampleFix":"// before\nconst preferences = unwrapEvaluateResult(await page.evaluate(buildPreferencesScript()));\nreturn out(normalizePreferences(preferences, alerts));\n// after\nconst preferences = unwrapEvaluateResult(await page.evaluate(buildPreferencesScript()));\nif (!preferences || typeof preferences !== 'object') {\n  await page.screenshot({ path: 'preferences-debug.png' });\n}\nreturn out(normalizePreferences(preferences, alerts));","handlingStrategy":"type-guard","validationCode":"const prefs = unwrapEvaluateResult(await page.evaluate(buildPreferencesScript()));\nif (!prefs || typeof prefs !== 'object' || Array.isArray(prefs)) {\n  await page.screenshot({ path: 'prefs-debug.png' });\n  throw new Error('preferences extraction returned non-object — likely authwall or DOM change');\n}","typeGuard":"function isPreferencesPayload(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) && ('raw_preferences' in v || 'job_titles' in v);\n}","tryCatchPattern":"try {\n  return normalizePreferences(preferences, alerts);\n} catch (err) {\n  if (String(err.message).includes('malformed preferences payload')) {\n    await assertLinkedInAuthenticated(page, 'preferences');\n    // retry extraction once after re-auth / longer wait\n  } else throw err;\n}","preventionTips":["Verify authentication before evaluating extraction scripts","Use generous waits after page.goto for LinkedIn's dynamic content","Pin/monitor LinkedIn DOM changes; test the extraction script after LinkedIn UI updates","Log raw evaluate output in debug mode for post-mortem"],"tags":["linkedin","schema-validation","scraping"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}