jackwener/OpenCLI · error · AuthRequiredError

linkedin.com: LinkedIn li_at cookie missing

Error message

linkedin.com: LinkedIn li_at cookie missing

What it means

verifyLinkedinLearningIdentity checks the Puppeteer page for a non-empty LinkedIn 'li_at' session cookie before probing the Learning API. Without it, LinkedIn Learning endpoints redirect to a login page, so the command throws AuthRequiredError early. This tells the user they must log in to LinkedIn in the automation browser first.

Source

Thrown at clis/linkedin-learning/auth.js:11

import { AuthRequiredError, CommandExecutionError } from '@jackwener/opencli/errors';
import { registerSiteAuthCommands } from '../_shared/site-auth.js';

async function hasLinkedinSessionCookie(page) {
  const cookies = await page.getCookies({ url: 'https://www.linkedin.com' });
  return cookies.some(c => c.name === 'li_at' && c.value);
}

async function verifyLinkedinLearningIdentity(page) {
  if (!await hasLinkedinSessionCookie(page)) {
    throw new AuthRequiredError('linkedin.com', 'LinkedIn li_at cookie missing');
  }
  await page.goto('https://www.linkedin.com/learning/');
  await page.wait(2);
  const result = await page.evaluate(`(async () => {
    try {
      const jsessionRaw = (document.cookie.split('; ').find(c => c.startsWith('JSESSIONID=')) || '').split('=')[1] || '';
      const csrf = jsessionRaw.replace(/^"|"$/g, '');
      if (!csrf) return { kind: 'auth', detail: 'LinkedIn JSESSIONID missing — csrf token unavailable' };
      const res = await fetch('/voyager/api/me', { credentials: 'include', headers: { 'csrf-token': csrf, 'Accept': 'application/json' } });
      if (res.status === 401 || res.status === 403) {
        return { kind: 'auth', detail: 'LinkedIn /voyager/api/me HTTP ' + res.status };
      }
      if (!res.ok) return { kind: 'http', httpStatus: res.status };
      const d = await res.json();
      const mini = d && d.miniProfile;
      if (!mini || !mini.publicIdentifier) {
        return { kind: 'auth', detail: 'LinkedIn /voyager/api/me 200 but miniProfile missing' };
      }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Run the site's login flow (the registered loginUrl https://www.linkedin.com/login?session_redirect=%2Flearning%2F) and complete LinkedIn login in the automation browser.
  2. Confirm li_at exists after login: document.cookie or page.getCookies shows li_at with a value.
  3. If the session expired, log in again to get a fresh li_at cookie.
  4. Ensure the browser profile used persists cookies between runs.

Example fix

// before
await runCommand('linkedin-learning course <slug>'); // fails: no session
// after
await runCommand('linkedin-learning login'); // complete LinkedIn login in the browser
const identity = await runCommand('linkedin-learning whoami');
Defensive patterns

Strategy: validation

Validate before calling

const cookies = await page.getCookies({ url: 'https://www.linkedin.com' });
const hasLiAt = cookies.some(c => c.name === 'li_at' && c.value);
if (!hasLiAt) {
  await startLoginFlow('https://www.linkedin.com/login?session_redirect=%2Flearning%2F');
}

Type guard

function hasLiAtCookie(cookies) {
  return Array.isArray(cookies) && cookies.some(c => c?.name === 'li_at' && typeof c.value === 'string' && c.value.length > 0);
}

Try / catch

try {
  const identity = await verifyLinkedinLearningIdentity(page);
} catch (e) {
  if (e.name === 'AuthRequiredError' && e.message.includes('li_at')) {
    await runInteractiveLogin('linkedin-learning');
    return verifyLinkedinLearningIdentity(page);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any linkedin-learning command (via registerSiteAuthCommands verify flow) when page.getCookies({ url: 'https://www.linkedin.com' }) contains no 'li_at' cookie with a non-empty value.

Common situations: First run of the tool before ever logging in; expired LinkedIn session (li_at rotated/cleared); running with a fresh browser profile or in CI where no cookies exist; user logged out of LinkedIn elsewhere invalidating the session.

Related errors


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