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 article command reads the x.com CSRF token (ct0 cookie) from the browser session via CDP before making authenticated API calls. If no ct0 cookie exists, it throws AuthRequiredError('x.com'), meaning the browser session is not logged into x.com and API requests would be rejected.

Source

Thrown at clis/twitter/article.js:64

            if (m2) return m2[1];
          }
          return null;
        })()
      `);
            const resolvedTweetId = unwrapBrowserResult(resolvedId);
            if (!resolvedTweetId || typeof resolvedTweetId !== 'string') {
                throw new CommandExecutionError(`Could not resolve article ${tweetId} to a tweet ID. The article page may not contain a linked tweet.`);
            }
            tweetId = resolvedTweetId;
        }
        // Navigate to the tweet page for cookie context
        await page.goto(`https://x.com/i/status/${tweetId}`);
        await page.wait(3);
        // Read CSRF token directly from the cookie store via CDP — zero page.evaluate round-trip
        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, 'TweetResultByRestId', TWEET_RESULT_BY_REST_ID_QUERY_ID);
        const rawResult = unwrapBrowserResult(await page.evaluate(`
      async () => {
        const tweetId = ${JSON.stringify(tweetId)};
        const ct0 = ${JSON.stringify(ct0)};

        const bearer = ${JSON.stringify(TWITTER_BEARER_TOKEN)};
        const headers = {
          'Authorization': 'Bearer ' + decodeURIComponent(bearer),
          'X-Csrf-Token': ct0,
          'X-Twitter-Auth-Type': 'OAuth2Session',
          'X-Twitter-Active-User': 'yes'
        };

        const variables = JSON.stringify({
          tweetId: tweetId,
          withCommunity: false,
          includePromotedContent: false,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Run the twitter login command (or log into x.com in the CLI's browser) before article
  2. Re-login if your x.com session expired — verify ct0 exists via browser devtools
  3. Point the command at the profile where you are actually logged in
  4. Avoid clearing cookies for x.com between runs

Example fix

// before
node cli.js twitter article <id>   // fresh profile, no login
// after
node cli.js twitter login
node cli.js twitter article <id>
Defensive patterns

Strategy: validation

Validate before calling

// before invoking article, confirm the session has the ct0 cookie
const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.some((c) => c.name === 'ct0')) {
  throw new Error('Run `twitter login` first — no x.com session');
}

Type guard

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

Try / catch

import { AuthRequiredError } from '@jackwener/opencli/errors';
try {
  await articleCmd({ tweetId });
} catch (err) {
  if (err instanceof AuthRequiredError) {
    console.error('Not logged into x.com — run twitter login and retry');
  } else throw err;
}

Prevention

When it happens

Trigger: Running twitter article with a browser profile that has no logged-in x.com session — cookies for https://x.com contain no ct0 entry.

Common situations: Fresh/never-logged-in browser profile, x.com session expired since last login, logging out of x.com manually, running in a CI/headless container with an empty profile, or cookies cleared between runs.

Related errors


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