{"record":{"id":"08e9a1932438e229","repo":"jackwener/OpenCLI","slug":"linkedin-jobs-preferences-returned-malformed-alert","errorCode":null,"errorMessage":"LinkedIn jobs preferences returned malformed alerts payload","messagePattern":"LinkedIn jobs preferences returned malformed alerts payload","errorType":"error_code","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/jobs-preferences.js","lineNumber":68,"sourceCode":"      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  };\n}\n\ncli({","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/jobs-preferences.js#L50-L86","documentation":"Thrown by normalizePreferences when the alerts payload (extracted from LinkedIn's job alerts page) is not a non-null object. Like the preferences guard, it rejects malformed scrape results before reading raw_preferences. The second page navigation (ALERTS_URL) returned something other than the expected object.","triggerScenarios":"page.evaluate on ALERTS_URL returning null/undefined/primitive because the alerts page didn't load its data (authwall, empty alerts state, DOM change) or unwrapEvaluateResult flattened an unexpected shape.","commonSituations":"User has never configured job alerts so the alerts page renders no structured payload; redirect to login after cookie expiry mid-run; LinkedIn A/B-tested markup so the extraction script returns null.","solutions":["Verify the session stayed authenticated through the second page.goto(ALERTS_URL)","Increase wait time after navigating to ALERTS_URL","Create/save at least one job alert or update buildAlertsScript() for the empty-state DOM","Log the raw alerts evaluate result to distinguish empty-state vs authwall vs markup change"],"exampleFix":"// before\nconst alerts = unwrapEvaluateResult(await page.evaluate(buildAlertsScript()));\nreturn out(normalizePreferences(preferences, alerts));\n// after\nconst alerts = unwrapEvaluateResult(await page.evaluate(buildAlertsScript()));\nif (!alerts || typeof alerts !== 'object') {\n  alerts = { raw_preferences: '', job_alerts: [] };\n}\nreturn out(normalizePreferences(preferences, alerts));","handlingStrategy":"type-guard","validationCode":"const alertData = unwrapEvaluateResult(await page.evaluate(buildAlertsScript()));\nif (!alertData || typeof alertData !== 'object' || Array.isArray(alertData)) {\n  throw new Error('alerts extraction returned non-object — check ALERTS_URL page load/auth');\n}","typeGuard":"function isAlertsPayload(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) && ('raw_preferences' in v || 'job_alerts' in v);\n}","tryCatchPattern":"try {\n  return normalizePreferences(preferences, alerts);\n} catch (err) {\n  if (String(err.message).includes('malformed alerts payload')) {\n    await page.goto(ALERTS_URL);\n    await page.wait(8);\n    alerts = unwrapEvaluateResult(await page.evaluate(buildAlertsScript()));\n  } else throw err;\n}","preventionTips":["Re-check authentication before the second navigation — cookies can invalidate mid-run","Wait adequately after ALERTS_URL navigation before evaluate","Create at least one job alert on the account to avoid empty-state payloads","Keep the alerts extraction script updated against LinkedIn DOM changes"],"tags":["linkedin","schema-validation","scraping"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}