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
- Check httpStatus: 403 usually means missing/invalid csrf-token or anti-bot block; 429 means slow down and retry.
- Ensure the probe sends the JSESSIONID-derived csrf-token header and referer headers exactly as a logged-in browser does.
- Retry after a delay if 429/5xx.
- 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
- Always send the JSESSIONID-derived csrf-token header to Voyager endpoints.
- Throttle repeated probes to avoid 429s.
- Use a realistic user-agent and headful mode to dodge anti-bot blocks.
- Map statuses to actions: 403 = auth/headers, 429 = backoff, 5xx = retry.
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
- LinkedIn Learning searchV2 failed: ${result?.error ?? 'no pa
- LinkedIn Learning feedRecommendationGroups failed: ${result?
- ${label} returned HTTP ${resp.status}: ${summarizeApiError(p
- archive search failed: HTTP ${resp.status}
- HTTP ${result.httpStatus} from /api/organizations
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/d101279d4ab9079f.
Report an issue: GitHub.