{"record":{"id":"e74f56367168ffed","repo":"jackwener/OpenCLI","slug":"not-logged-into-x-com-no-ct0-cookie-e74f56","errorCode":null,"errorMessage":"Not logged into x.com (no ct0 cookie)","messagePattern":"Not logged into x\\.com \\(no ct0 cookie\\)","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/twitter/bookmarks.js","lineNumber":207,"sourceCode":"        const useOutputFile = Boolean(fetchAll && outputFile);\n        const maxPages = resolveMaxPages(kwargs, fetchAll);\n        const topByEngagement = Number(kwargs['top-by-engagement'] || 0);\n        if (useOutputFile && topByEngagement > 0) {\n            throw new ArgumentError('--top-by-engagement cannot be combined with --output-file');\n        }\n        if (outputFile && !fetchAll) {\n            throw new ArgumentError('--output-file requires --all');\n        }\n        if (resumeFile && !fetchAll) {\n            throw new ArgumentError('--resume-file requires --all');\n        }\n        if (outputFile && !resumeFile) {\n            throw new ArgumentError('--output-file requires --resume-file so partial archives remain resumable');\n        }\n        const cookies = await page.getCookies({ url: 'https://x.com' });\n        const ct0 = cookies.find((c) => c.name === 'ct0')?.value || null;\n        if (!ct0)\n            throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');\n        const queryId = await resolveTwitterQueryId(page, 'Bookmarks', BOOKMARKS_QUERY_ID);\n        const headers = JSON.stringify({\n            'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,\n            'X-Csrf-Token': ct0,\n            'X-Twitter-Auth-Type': 'OAuth2Session',\n            'X-Twitter-Active-User': 'yes',\n        });\n        const resumed = fetchAll ? readResumeFile(resumeFile, {\n            source: 'bookmarks',\n            outputFile: useOutputFile ? outputFile : null,\n        }) : null;\n        if (useOutputFile && resumed && resumed.count > 0 && !fs.existsSync(outputFile)) {\n            throw new CommandExecutionError(`Twitter bookmarks output file is missing for resume state: ${outputFile}`);\n        }\n        if (useOutputFile && !resumed && fs.existsSync(outputFile)) {\n            throw new ArgumentError(`Refusing to overwrite existing Twitter bookmarks output file: ${outputFile}`);\n        }\n        const allTweets = useOutputFile ? [] : (resumed?.tweets ? [...resumed.tweets] : []);","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/bookmarks.js#L189-L225","documentation":"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.","triggerScenarios":"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' })`.","commonSituations":"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.","solutions":["Log into x.com in the browser/profile the CLI drives, then re-run the command.","Verify the ct0 cookie exists: page.getCookies({ url: 'https://x.com' }) should include a cookie named 'ct0'.","Use a persistent browser profile (not incognito) so the session survives restarts.","Re-authenticate if the session expired, then retry."],"exampleFix":"// before: fresh context with no login\nconst context = await browser.newContext();\n// after: use the logged-in persistent profile\nconst context = await browser.newContext({ storageState: 'x-auth.json' });","handlingStrategy":"validation","validationCode":"const cookies = await page.getCookies({ url: 'https://x.com' });\nif (!cookies.some((c) => c.name === 'ct0')) {\n  throw new Error('Not logged into x.com (no ct0 cookie) — login before running');\n}","typeGuard":"function hasCt0Cookie(cookies) {\n  return Array.isArray(cookies) && cookies.some((c) => c && c.name === 'ct0' && typeof c.value === 'string' && c.value.length > 0);\n}","tryCatchPattern":"try {\n  await runBookmarks(argv);\n} catch (e) {\n  if (e instanceof AuthRequiredError) {\n    console.error(`Login required for ${e.domain ?? 'x.com'}: ${e.message}. Open the browser profile and log in, then retry.`);\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["Use a persistent browser profile with a valid x.com session, not incognito.","Check for the ct0 cookie as a preflight step before any x.com automation.","Re-login proactively when sessions age out; monitor auth failures in logs.","Save/restore storageState so sessions survive browser restarts."],"tags":["auth","twitter","cookies","session-expired"],"backgroundTag":"missing-auth-cookie","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}