jackwener/OpenCLI · error · AuthRequiredError
Not logged into x.com (no ct0 cookie)
Error message
Not logged into x.com (no ct0 cookie)
What it means
The UserMedia GraphQL flow authenticates with the browser's ct0 cookie as the X-CSRF-Token header. Before querying, the code reads cookies for https://x.com and throws AuthRequiredError if no ct0 cookie exists, because x.com only issues ct0 to logged-in sessions and GraphQL calls would fail without it.
Source
Thrown at clis/twitter/download.js:351
if (!username) {
throw new ArgumentError('twitter download username must be a valid Twitter/X handle', 'Example: opencli twitter download @jack --limit 20');
}
return downloadUserMedia(page, username, limit, output);
}
catch (err) {
if (err instanceof CliError) throw err;
throw new CommandExecutionError(`twitter download failed: ${err?.message ?? String(err)}`);
}
},
});
async function downloadUserMedia(page, username, limit, output) {
await page.goto(`https://x.com/${username}`);
await page.wait({ selector: '[data-testid="primaryColumn"]' });
const cookies = await page.getCookies({ url: 'https://x.com' });
const ct0 = cookies.find((c) => c.name === 'ct0')?.value || null;
if (!ct0) throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');
const userMediaOperation = await resolveTwitterOperationMetadata(page, 'UserMedia', USER_MEDIA_OPERATION);
const userByScreenNameOperation = await resolveTwitterOperationMetadata(page, 'UserByScreenName', USER_BY_SCREEN_NAME_OPERATION);
const headers = JSON.stringify({
'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,
'X-Csrf-Token': ct0,
'X-Twitter-Auth-Type': 'OAuth2Session',
'X-Twitter-Active-User': 'yes',
});
const ubsUrl = buildUserByScreenNameUrl(userByScreenNameOperation, username);
const userLookup = requireFetchPayload(await page.evaluate(`async () => {
try {
const resp = await fetch("${ubsUrl}", { headers: ${headers}, credentials: 'include' });
if (!resp.ok) return { ok: false, status: resp.status };
const payload = await resp.json();
return { ok: true, payload };View on GitHub (pinned to 49907e53dc)
Solutions
- Log into x.com in the browser profile the CLI uses, then re-run the command
- Verify the CLI is pointed at the correct browser profile/user-data-dir containing the logged-in session
- Clear cookies and log in again if the session is corrupt or expired
Defensive patterns
Strategy: validation
Validate before calling
const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.some((c) => c.name === 'ct0')) {
throw new Error('Log into x.com in the browser profile first');
} Type guard
const hasCt0 = (cookies) => Array.isArray(cookies) && cookies.some((c) => c.name === 'ct0' && c.value);
Try / catch
try {
await downloadUserMedia(page, username, limit);
} catch (err) {
if (err.name === 'AuthRequiredError') {
console.error('Open a browser on x.com and log in, then retry.');
}
} Prevention
- Log into x.com in the exact browser profile the CLI uses
- Re-login when sessions expire; ct0 is rotated on logout/expiry
- Do not share profiles across machines where cookies may be cleared
When it happens
Trigger: Running `opencli twitter download <username>` with a browser profile that has never logged into x.com, after logging out, or with cookies cleared/expired so no ct0 cookie is present.
Common situations: Fresh automation browser profile without a login; session expired (x.com rotates/invalidates ct0); pointing the CLI at the wrong profile directory; corporate proxy stripping cookies.
Related errors
- Not logged into x.com (no ct0 cookie)
- Not logged into x.com (no ct0 cookie)
- Not logged into x.com (no ct0 cookie)
- Not logged into x.com (no ct0 cookie)
- Not logged into x.com (no ct0 cookie)
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/fd58da0d0266ddc8.
Report an issue: GitHub.