jackwener/OpenCLI · critical · AuthRequiredError
Not logged into x.com (no ct0 cookie)
Error message
Not logged into x.com (no ct0 cookie)
What it means
After loading https://x.com/home the command inspects cookies for ct0, X's CSRF token which is only set for authenticated sessions. If no ct0 cookie exists, the session is not logged in, and scraping the followers API is impossible, so an AuthRequiredError is thrown. This is the library's explicit authentication precondition check.
Source
Thrown at clis/twitter/followers.js:109
// when a dedicated follower count is needed.
columns: ['screen_name', 'name', 'bio'],
func: async (page, kwargs) => {
const limit = kwargs.limit;
if (!Number.isInteger(limit) || limit <= 0) {
throw new ArgumentError('limit must be a positive integer');
}
const rawUser = String(kwargs.user ?? '').trim();
let targetUser = normalizeScreenName(rawUser);
if (rawUser && !targetUser) {
throw new ArgumentError('twitter followers user must be a valid Twitter/X handle', 'Example: opencli twitter followers @elonmusk --limit 100');
}
await page.goto('https://x.com/home');
await page.wait({ selector: '[data-testid="primaryColumn"]' });
const cookies = await page.getCookies({ url: 'https://x.com' });
const ct0 = cookies.find((cookie) => cookie.name === 'ct0')?.value || null;
if (!ct0) {
throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');
}
if (!targetUser) {
// Bridge wraps primitive page.evaluate returns as { session, data:<value> };
// unwrap so the href string is usable downstream.
const href = unwrapBrowserResult(await page.evaluate(`() => {
const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
return link ? link.getAttribute('href') : null;
}`));
if (!href || typeof href !== 'string') {
throw new AuthRequiredError('x.com', 'Could not find logged-in user profile link. Are you logged in?');
}
targetUser = normalizeScreenName(href);
if (!targetUser) {
throw new AuthRequiredError('x.com', 'Could not find logged-in user profile link. Are you logged in?');
}
}
if (!targetUser) {View on GitHub (pinned to 49907e53dc)
Solutions
- Log into x.com in the browser profile the tool uses, then re-run
- Point the tool at the persistent browser profile/user-data-dir containing your session
- Re-authenticate if your session expired (log in again to regenerate ct0)
- Verify the cookie exists: document.cookie or devtools Application tab for x.com
Example fix
// before (fresh headless context, no login)
page = await browser.newContext() // no x.com session
// after (reuse logged-in profile)
page = await browser.newContext({ storageState: 'xcom-session.json' }) Defensive patterns
Strategy: try-catch
Validate before calling
const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.some(c => c.name === 'ct0')) {
throw new Error('Run: opencli auth twitter (or log into x.com in the browser profile)');
} Type guard
null
Try / catch
try {
const rows = await opencli.twitter.followers(user);
} catch (err) {
if (err instanceof AuthRequiredError || /no ct0 cookie/.test(err.message)) {
await loginToX(); // open browser for interactive login, then retry once
} else throw err;
} Prevention
- Log into x.com in the persistent browser profile the tool uses
- Check for the ct0 cookie before running batch jobs
- Re-authenticate after password changes; X expires sessions
- Avoid incognito/ephemeral contexts for authenticated commands
When it happens
Trigger: Running the command with a browser profile that has never logged into x.com; cookies were cleared or expired; the browser context is fresh/incognito; x.com session was invalidated server-side.
Common situations: CI environments with no logged-in browser profile; forgetting to point the tool at the persistent profile where you logged in; X logged you out after a password change or security event.
Related errors
- Not logged into x.com (no ct0 cookie)
- Waiting for Bilibili session cookies
- bilibili.com
- creator.douyin.com
- Waiting for Manus session cookies
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/de7fa401b6f4539c.
Report an issue: GitHub.