{"record":{"id":"2c126e6be2d4084f","repo":"jackwener/OpenCLI","slug":"browser-session-required-for-twitter-follow-batch","errorCode":null,"errorMessage":"Browser session required for twitter follow-batch","messagePattern":"Browser session required for twitter follow-batch","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/follow-batch.js","lineNumber":142,"sourceCode":"    return value;\n}\n\ncli({\n    site: 'twitter',\n    name: 'follow-batch',\n    access: 'write',\n    description: 'Follow multiple Twitter/X users from a comma-separated username list',\n    domain: 'x.com',\n    strategy: Strategy.UI,\n    browser: true,\n    args: [\n        { name: 'usernames', type: 'string', positional: true, required: true, help: 'Comma-separated Twitter/X screen names, with or without @' },\n        { name: 'delay-ms', type: 'int', default: DEFAULT_DELAY_MS, help: 'Delay between follow attempts in milliseconds' },\n    ],\n    columns: ['username', 'status', 'message'],\n    func: async (page, kwargs) => {\n        if (!page) {\n            throw new CommandExecutionError('Browser session required for twitter follow-batch');\n        }\n\n        const usernames = parseBatchUsernames(kwargs.usernames);\n        const delayMs = parseDelayMs(kwargs['delay-ms']);\n        const cookies = await page.getCookies({ url: 'https://x.com' });\n        const ct0 = cookies.find((cookie) => cookie.name === 'ct0')?.value || null;\n        if (!ct0) {\n            throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');\n        }\n\n        const rows = [];\n        for (const [index, username] of usernames.entries()) {\n            if (index > 0 && delayMs > 0) {\n                await page.wait(delayMs / 1000);\n            }\n            rows.push(await followOne(page, username));\n        }\n        return rows;","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/follow-batch.js#L124-L160","documentation":"The twitter follow-batch command performs all work through a live browser Page (cookies via page.getCookies, in-page evaluation on x.com), so it throws CommandExecutionError when invoked with no browser session (page is null). The CLI was run without the browser daemon/extension connected, or the command's func was called programmatically without a page.","triggerScenarios":"Running `opencli twitter follow-batch ...` with no connected browser session (daemon not running, extension not connected); invoking func(page, kwargs) directly with page = null; the browser session dropped before the command body executed.","commonSituations":"Forgetting to start the Chrome/Chromium daemon or connect the extension first; running in CI/headless environments with no browser attached; a prior command crashed the session so the next receives no page.","solutions":["Start/connect the browser session first (launch Chrome with the CLI's daemon/extension) and re-run the command.","Verify the daemon is running and the extension is connected before invoking write commands.","In scripts, check that a page/session object exists before calling the command's func.","Route through the normal CLI entrypoint so session setup happens automatically instead of calling func with a null page."],"exampleFix":"// before\nawait followBatch.func(null, { usernames: 'alice' })\n// after\nconst page = await connectBrowser(); // ensure session\nawait followBatch.func(page, { usernames: 'alice' })","handlingStrategy":"try-catch","validationCode":"// Check the session before invoking the command\nasync function ensureBrowserSession(getPage) {\n  const page = typeof getPage === 'function' ? await getPage() : getPage;\n  if (!page) throw new Error('Browser session required: start/connect Chrome before running twitter commands');\n  return page;\n}","typeGuard":"function hasPage(page) {\n  return page !== null && page !== undefined && typeof page.evaluate === 'function' && typeof page.getCookies === 'function';\n}","tryCatchPattern":"try {\n  await followBatch(usernames);\n} catch (e) {\n  if (e.code === 'COMMAND_EXEC' && e.message.includes('Browser session required')) {\n    await startOrConnectBrowser(); // daemon/extension\n    return retry(followBatch, usernames);\n  }\n  throw e;\n}","preventionTips":["Start the browser daemon and connect the extension before any browser-backed command.","In CI, provision and health-check the browser session as a setup step.","Check session liveness between long-running steps; reconnect after crashes.","Call commands through the CLI entrypoint rather than invoking func(page,...) with ad-hoc arguments."],"tags":["browser-session","twitter","command-execution","setup"],"backgroundTag":"browser-session-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}