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 command reads cookies for https://x.com and requires the `ct0` cookie, which Twitter/X sets on login (it is the CSRF token used with authenticated API calls). If no ct0 cookie exists, AuthRequiredError('x.com') is thrown. Without it, the follow POSTs to x.com's API would be rejected, so the command fails fast before attempting any follows.

Source

Thrown at clis/twitter/follow-batch.js:150

    domain: 'x.com',
    strategy: Strategy.UI,
    browser: true,
    args: [
        { name: 'usernames', type: 'string', positional: true, required: true, help: 'Comma-separated Twitter/X screen names, with or without @' },
        { name: 'delay-ms', type: 'int', default: DEFAULT_DELAY_MS, help: 'Delay between follow attempts in milliseconds' },
    ],
    columns: ['username', 'status', 'message'],
    func: async (page, kwargs) => {
        if (!page) {
            throw new CommandExecutionError('Browser session required for twitter follow-batch');
        }

        const usernames = parseBatchUsernames(kwargs.usernames);
        const delayMs = parseDelayMs(kwargs['delay-ms']);
        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)');
        }

        const rows = [];
        for (const [index, username] of usernames.entries()) {
            if (index > 0 && delayMs > 0) {
                await page.wait(delayMs / 1000);
            }
            rows.push(await followOne(page, username));
        }
        return rows;
    }
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open the connected Chrome/Chromium browser and log in to https://x.com, then re-run the command.
  2. Confirm the correct browser profile is being used if you have multiple profiles.
  3. If the session expired mid-run, re-login to refresh ct0 and other auth cookies.
  4. Avoid fresh --user-data-dir or cookie-clearing modes that wipe the x.com session.

Example fix

// before
// headless CI, never logged in -> no ct0
// after
// 1) launch the connected browser
// 2) log in to https://x.com
// 3) re-run: opencli twitter follow-batch 'alice,bob'
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: verify an x.com login cookie exists in the connected session
async function isLoggedInToX(page) {
  const cookies = await page.getCookies({ url: 'https://x.com' });
  return cookies.some((c) => c.name === 'ct0');
}
// if (!(await isLoggedInToX(page))) promptLogin('https://x.com');

Type guard

function hasCt0(cookies) {
  return Array.isArray(cookies) && cookies.some((c) => c && c.name === 'ct0' && !!c.value);
}

Try / catch

try {
  await followBatch(usernames);
} catch (e) {
  if (e.code === 'AUTH_REQUIRED' && e.domain === 'x.com') {
    console.error('Log in to https://x.com in the connected Chrome, then re-run.');
    await openBrowserAndPromptLogin('https://x.com');
    return retry(followBatch, usernames);
  }
  throw e;
}

Prevention

When it happens

Trigger: Running `twitter follow-batch` while the connected browser profile is logged out of x.com; cookies cleared or a fresh/incognito profile in use; page.getCookies({url:'https://x.com'}) returns cookies but none named ct0 (visited x.com without logging in); the X session expired.

Common situations: Automating on a headless box where no one ever logged into X; X logged the account out after a security prompt or password change; pointing the CLI at the wrong Chrome profile; wiping cookies between runs.

Related errors


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