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 bookmarks fetch authenticates using the browser session's ct0 CSRF cookie from x.com. clis/twitter/bookmarks.js:207 throws AuthRequiredError('x.com', ...) when page.getCookies() finds no ct0 cookie, meaning the browser page is not logged into x.com. Without ct0 the GraphQL request would be rejected, so the CLI fails fast.

Source

Thrown at clis/twitter/bookmarks.js:207

        const useOutputFile = Boolean(fetchAll && outputFile);
        const maxPages = resolveMaxPages(kwargs, fetchAll);
        const topByEngagement = Number(kwargs['top-by-engagement'] || 0);
        if (useOutputFile && topByEngagement > 0) {
            throw new ArgumentError('--top-by-engagement cannot be combined with --output-file');
        }
        if (outputFile && !fetchAll) {
            throw new ArgumentError('--output-file requires --all');
        }
        if (resumeFile && !fetchAll) {
            throw new ArgumentError('--resume-file requires --all');
        }
        if (outputFile && !resumeFile) {
            throw new ArgumentError('--output-file requires --resume-file so partial archives remain resumable');
        }
        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 queryId = await resolveTwitterQueryId(page, 'Bookmarks', BOOKMARKS_QUERY_ID);
        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 resumed = fetchAll ? readResumeFile(resumeFile, {
            source: 'bookmarks',
            outputFile: useOutputFile ? outputFile : null,
        }) : null;
        if (useOutputFile && resumed && resumed.count > 0 && !fs.existsSync(outputFile)) {
            throw new CommandExecutionError(`Twitter bookmarks output file is missing for resume state: ${outputFile}`);
        }
        if (useOutputFile && !resumed && fs.existsSync(outputFile)) {
            throw new ArgumentError(`Refusing to overwrite existing Twitter bookmarks output file: ${outputFile}`);
        }
        const allTweets = useOutputFile ? [] : (resumed?.tweets ? [...resumed.tweets] : []);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log into x.com in the browser/profile the CLI drives, then re-run the command.
  2. Verify the ct0 cookie exists: page.getCookies({ url: 'https://x.com' }) should include a cookie named 'ct0'.
  3. Use a persistent browser profile (not incognito) so the session survives restarts.
  4. Re-authenticate if the session expired, then retry.

Example fix

// before: fresh context with no login
const context = await browser.newContext();
// after: use the logged-in persistent profile
const context = await browser.newContext({ storageState: 'x-auth.json' });
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('Not logged into x.com (no ct0 cookie) — login before running');
}

Type guard

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

Try / catch

try {
  await runBookmarks(argv);
} catch (e) {
  if (e instanceof AuthRequiredError) {
    console.error(`Login required for ${e.domain ?? 'x.com'}: ${e.message}. Open the browser profile and log in, then retry.`);
    process.exitCode = 1;
  } else throw e;
}

Prevention

When it happens

Trigger: The automated browser page's cookie jar for https://x.com contains no cookie named ct0 — i.e. the page is logged out or the session was never established — detected right after `page.getCookies({ url: 'https://x.com' })`.

Common situations: Expired or revoked x.com login in the automation browser profile; running against a fresh/incognito browser context; corporate proxy or cookie clearing wiping session cookies; login flow changed and ct0 not yet set before the CLI reads cookies.

Related errors


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