jackwener/OpenCLI · error · CommandExecutionError

HTTP ${result.httpStatus} from /voyager/api/me

Error message

HTTP ${result.httpStatus} from /voyager/api/me

What it means

When the in-page probe reaches /voyager/api/me but receives an HTTP error status, the probe returns { kind:'http', httpStatus } and the command rethrows it as CommandExecutionError with the status embedded. This means the request was made with the browser's credentials but LinkedIn's API rejected it at the HTTP level.

Source

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

      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' };
      }
      const firstName = (mini.firstName && (mini.firstName.text || mini.firstName)) || '';
      const lastName = (mini.lastName && (mini.lastName.text || mini.lastName)) || '';
      return {
        ok: true,
        public_id: String(mini.publicIdentifier),
        plain_id: String(d.plainId || ''),
        name: String((firstName + ' ' + lastName).trim()),
      };
    } catch (e) {
      return { kind: 'exception', detail: String(e && e.message || e) };
    }
  })()`);
  if (result?.kind === 'auth') throw new AuthRequiredError('linkedin.com', result.detail);
  if (result?.kind === 'http') throw new CommandExecutionError(`HTTP ${result.httpStatus} from /voyager/api/me`);
  if (result?.kind === 'exception') throw new CommandExecutionError(`LinkedIn Learning whoami failed: ${result.detail}`);
  if (!result?.ok) throw new CommandExecutionError(`Unexpected LinkedIn Learning probe: ${JSON.stringify(result)}`);
  return { public_id: result.public_id, plain_id: result.plain_id, name: result.name };
}

registerSiteAuthCommands({
  site: 'linkedin-learning',
  domain: 'linkedin.com',
  loginUrl: 'https://www.linkedin.com/login?session_redirect=%2Flearning%2F',
  columns: ['public_id', 'plain_id', 'name'],
  quickCheck: hasLinkedinSessionCookie,
  verify: verifyLinkedinLearningIdentity,
  poll: async (page) => {
    if (!await hasLinkedinSessionCookie(page)) {
      throw new AuthRequiredError('linkedin.com', 'Waiting for LinkedIn li_at cookie');
    }
    return verifyLinkedinLearningIdentity(page);
  },

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check httpStatus: 403 usually means missing/invalid csrf-token or anti-bot block; 429 means slow down and retry.
  2. Ensure the probe sends the JSESSIONID-derived csrf-token header and referer headers exactly as a logged-in browser does.
  3. Retry after a delay if 429/5xx.
  4. Use a less automated-looking browser profile (real user-agent, headful mode) if LinkedIn is blocking the client.

Example fix

// before
// probe: fetch('/voyager/api/me') -> 403
// after
// probe: include csrf header
const csrf = (document.cookie.split('; ').find(c=>c.startsWith('JSESSIONID='))||'').split('=')[1] || '';
await fetch('/voyager/api/me', { headers: { 'csrf-token': decodeURIComponent(csrf), 'accept': 'application/json' } });
Defensive patterns

Strategy: retry

Validate before calling

const csrf = (document.cookie.split('; ').find(c => c.startsWith('JSESSIONID=')) || '').split('=')[1] || '';
if (!csrf) throw new Error('no JSESSIONID csrf token — Learning session missing');

Type guard

function isHttpKindResult(r) { return r?.kind === 'http' && Number.isInteger(r.httpStatus); }

Try / catch

try {
  const identity = await verifyLinkedinLearningIdentity(page);
} catch (e) {
  const m = /HTTP (\d{3}) from \/voyager\/api\/me/.exec(e.message);
  if (m) {
    const status = Number(m[1]);
    if (status === 429 || status >= 500) await sleep(30_000); // then retry
    else if (status === 403) await refreshCsrfAndProfile(page); // csrf/anti-bot issue
    else throw e;
  } else throw e;
}

Prevention

When it happens

Trigger: page.evaluate fetch of /voyager/api/me resolves with an error httpStatus (commonly 403 Forbidden, sometimes 429 or 5xx), producing result.kind === 'http'.

Common situations: LinkedIn blocking Voyager API calls without a valid csrf-token header after an API change; rate limiting from rapid repeated probes; LinkedIn WAF/anti-bot blocking the automation browser; region or account restrictions on Learning API.

Related errors


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