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
AuthRequiredError thrown before the search runs when the browser profile has no ct0 cookie for https://x.com. The ct0 cookie is X's CSRF token required to sign authenticated GraphQL requests (paired with the bearer token and X-Csrf-Token header), so its absence means the session is not logged in.
Source
Thrown at clis/twitter/search.js:289
{ name: 'product', type: 'string', choices: PRODUCT_CHOICES, help: 'Which X search tab to read: top (default), live (Latest), photos, videos. Maps to the f= URL param.' },
{ name: 'from', type: 'string', help: 'Restrict to tweets authored by <user>. Leading @ is stripped. Equivalent to appending `from:<user>` to the query.' },
{ name: 'has', type: 'string', choices: HAS_CHOICES, help: 'Restrict to tweets that have media|images|videos|links|replies. Maps to X\'s `filter:<has>` operator.' },
{ name: 'exclude', type: 'string', choices: EXCLUDE_CHOICES, help: 'Exclude tweets matching <type>: replies|retweets|media|links. Maps to X\'s `-filter:<x>` operator (retweets → -filter:nativeretweets).' },
{ name: 'limit', type: 'int', default: 15, help: 'Maximum number of tweets to return (default 15). Result count after server-side filtering.' },
{ name: 'top-by-engagement', type: 'int', default: 0, help: 'When set to N>0, re-rank the results by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X\'s native ordering.' },
],
columns: ['id', 'author', 'bio', 'text', 'created_at', 'likes', 'views', 'url', 'has_media', 'media_urls', 'media_posters', 'card', 'quoted_tweet'],
func: async (page, kwargs) => {
const finalQuery = buildSearchQuery(kwargs.query, kwargs);
if (!finalQuery) {
throw new ArgumentError('twitter search query is empty', 'Provide a non-empty <query>, or use at least one of --from / --has / --exclude.');
}
if (!Number.isInteger(Number(kwargs.limit)) || Number(kwargs.limit) <= 0) {
throw new ArgumentError('twitter search --limit must be a positive integer', 'Example: opencli twitter search opencli --limit 15');
}
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.goto('https://x.com/home', { waitUntil: 'load', settleMs: 1000 });
const operation = await resolveTwitterOperationMetadata(page, 'SearchTimeline', SEARCH_TIMELINE_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',
'Content-Type': 'application/json',
});
const product = resolveSearchProduct(kwargs);
const results = [];
const seen = new Set();
let cursor = null;
// Runaway guard only; --limit and cursor exhaustion control normal pagination.
for (let i = 0; i < MAX_PAGINATION_PAGES && results.length < kwargs.limit; i++) {
const fetchCount = Number(kwargs.limit) - results.length + 10;
const [requestUrl, requestPayload] = buildSearchTimelineRequest(operation, finalQuery, product, fetchCount, cursor);
const requestBody = JSON.stringify(requestPayload);View on GitHub (pinned to 49907e53dc)
Solutions
- Log into x.com in the browser session opencli uses, then re-run the command
- Refresh/restore the session cookies (ct0 and auth_token)
- Point opencli at the correct browser profile that holds the x.com session
- Re-run `opencli login`/auth flow if the CLI provides one
Example fix
// before opencli twitter search opencli // AuthRequiredError: no ct0 cookie // after opencli login # or manually log into x.com in the controlled browser opencli twitter search opencli
Defensive patterns
Strategy: validation
Validate before calling
const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.find((c) => c.name === 'ct0')) {
throw new Error('Not logged into x.com — run the login flow first');
} Type guard
function hasXSession(cookies) { return Array.isArray(cookies) && cookies.some((c) => c.name === 'ct0' && c.value); } Try / catch
try {
await opencli.twitter.search(q);
} catch (e) {
if (e.name === 'AuthRequiredError') {
await runXLoginFlow(); // open x.com, log in, persist cookies
await opencli.twitter.search(q);
} else throw e;
} Prevention
- Log into x.com in the automation browser profile before scripted runs
- Persist and reuse the profile that holds the session cookies
- Re-check ct0 freshness at the start of long jobs
- Avoid clearing cookies between runs
When it happens
Trigger: Running `opencli twitter search ...` with a fresh/incognito browser profile, after x.com logout, after cookies were cleared or expired, or when page.getCookies returns no x.com cookies.
Common situations: First run without ever logging into x.com in the automation browser, cookie purge by browser settings, logging out manually, or pointing opencli at the wrong profile directory.
Related errors
- Not logged into x.com (no ct0 cookie)
- Not logged into x.com (no ct0 cookie)
- 12306 tk auth cookie missing
- Waiting for Bilibili session cookies
- Bilibili ${label} API requires login or permission: ${messag
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/2af5f68821b29eec.
Report an issue: GitHub.