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

trending.js:33 verifies the x.com session before scraping the trending page by looking for the ct0 CSRF cookie in the cookie store for https://x.com. Its absence means the user is not logged in, so AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)') is thrown — trending content on x.com generally requires a session.

Source

Thrown at clis/twitter/trending.js:33

    access: 'read',
    description: 'Twitter/X trending topics',
    domain: 'x.com',
    strategy: Strategy.COOKIE,
    browser: true,
    args: [
        { name: 'limit', type: 'int', default: 20, help: 'Number of trends to show' },
    ],
    columns: ['rank', 'topic', 'category'],
    func: async (page, kwargs) => {
        const limit = kwargs.limit || 20;
        // Navigate to trending page
        await page.goto('https://x.com/explore/tabs/trending');
        await page.wait(3);
        // Verify login via CSRF cookie (read directly from cookie store via CDP)
        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)');
        await page.wait(2);
        const trends = await page.evaluate(`(() => {
      const items = [];
      const cells = document.querySelectorAll('[data-testid="trend"]');
      cells.forEach((cell) => {
        const text = cell.textContent || '';
        if (text.includes('Promoted')) return;
        const container = cell.querySelector(':scope > div');
        if (!container) return;
        const divs = container.children;
        if (divs.length < 2) return;
        const topic = divs[1].textContent.trim();
        if (!topic) return;
        const catText = divs[0].textContent.trim();
        const category = catText.replace(/^\\d+\\s*/, '').replace(/^\\xB7\\s*/, '').trim();
        items.push({ rank: items.length + 1, topic, category });
      });
      return items;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log into x.com in the browser profile the CLI controls, then rerun `opencli twitter trending`
  2. Verify the ct0 cookie exists for https://x.com in the profile (DevTools > Application > Cookies)
  3. If injecting cookies, add both auth_token and ct0 scoped to domain .x.com before navigation
  4. Re-run after x.com-initiated logouts — the cookie check will keep failing until a fresh login establishes ct0
Defensive patterns

Strategy: validation

Validate before calling

const cookies = await page.getCookies({ url: 'https://x.com' });
const ct0 = cookies.find((c) => c.name === 'ct0')?.value || null;
if (!ct0) throw new Error('Not logged into x.com: authenticate the CLI browser profile before trending fetch');

Type guard

const isLoggedInToX = (cookies) =>
  Array.isArray(cookies) && cookies.some((c) => c.name === 'ct0' && Boolean(c.value));

Try / catch

import { AuthRequiredError } from '@jackwener/opencli/errors';
try {
  const trends = await getTrending({ limit: 20 });
} catch (e) {
  if (e instanceof AuthRequiredError) {
    console.error('Login to x.com in the CLI browser profile, then rerun.');
  } else throw e;
}

Prevention

When it happens

Trigger: Running `opencli twitter trending` when the driven browser profile is logged out of x.com: no session at all, expired session, cleared cookies, or the pre-nav to https://x.com/explore/tabs/trending redirected to the login page.

Common situations: Fresh environment with no logged-in profile; x.com forced a re-login; cookie injection targeting twitter.com instead of x.com; corporate proxy or region redirect breaking the login; automation-detected session termination.

Related errors


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