{"record":{"id":"ecdc65e1c46bb6f2","repo":"jackwener/OpenCLI","slug":"browser-session-required-for-twitter-unfollow","errorCode":null,"errorMessage":"Browser session required for twitter unfollow","messagePattern":"Browser session required for twitter unfollow","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/unfollow.js","lineNumber":17,"sourceCode":"import { cli, Strategy } from '@jackwener/opencli/registry';\nimport { CommandExecutionError, TimeoutError } from '@jackwener/opencli/errors';\ncli({\n    site: 'twitter',\n    name: 'unfollow',\n    access: 'write',\n    description: 'Unfollow a Twitter user',\n    domain: 'x.com',\n    strategy: Strategy.UI,\n    browser: true,\n    args: [\n        { name: 'username', type: 'string', positional: true, required: true, help: 'Twitter screen name (without @)' },\n    ],\n    columns: ['status', 'message'],\n    func: async (page, kwargs) => {\n        if (!page)\n            throw new CommandExecutionError('Browser session required for twitter unfollow');\n        const username = kwargs.username.replace(/^@/, '');\n        await page.goto(`https://x.com/${username}`);\n        await page.wait({ selector: '[data-testid=\"primaryColumn\"]' });\n        const result = await page.evaluate(`(async () => {\n        let writeStarted = false;\n        try {\n            let attempts = 0;\n            let unfollowBtn = null;\n\n            while (attempts < 20) {\n                // Check if already not following\n                const followBtn = document.querySelector('[data-testid$=\"-follow\"]');\n                if (followBtn) {\n                    return { ok: true, message: 'Not following @${username} (already unfollowed).' };\n                }\n\n                unfollowBtn = document.querySelector('[data-testid$=\"-unfollow\"]');\n                if (unfollowBtn) break;","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/unfollow.js#L1-L35","documentation":"CommandExecutionError (code COMMAND_EXEC) thrown by `twitter unfollow` because unfollowing is a browser-write command needing an interactive page tied to the logged-in X session. When page is null the guard throws immediately rather than attempting the action. Identical guard pattern to twitter unblock and unbookmark.","triggerScenarios":"Running `twitter unfollow <username>` without an active browser session, so `func` receives page === null/undefined at the guard on line 17.","commonSituations":"Scripted/headless invocation without connecting the browser; CI with no logged-in Chrome; forgetting the x.com login/bootstrap step before write commands.","solutions":["Start/connect a browser session and log in to x.com, then re-run `twitter unfollow`","Run the command in interactive mode so a page is passed to the command's func","In scripts, check browser availability before invoking and fail with a clear prerequisite message"],"exampleFix":"// before\nawait cli.run(['twitter', 'unfollow', 'user']); // no browser session\n// after\nconst page = await getBrowserPage('x.com');\nif (!page) throw new Error('Start a browser session and log in to x.com first');\nawait cli.run(['twitter', 'unfollow', 'user']);","handlingStrategy":"validation","validationCode":"if (!browserSession || !browserSession.page) {\n  throw new Error('Start a browser session and log in to x.com before running twitter unfollow');\n}","typeGuard":"function hasBrowserPage(ctx) { return ctx != null && typeof ctx.page === 'object' && ctx.page !== null; }","tryCatchPattern":"try {\n  await cli.run(['twitter', 'unfollow', user]);\n} catch (e) {\n  if (e.code === 'COMMAND_EXEC' && /Browser session required/.test(e.message)) {\n    console.error('Connect a browser session and log in to x.com first');\n  } else throw e;\n}","preventionTips":["Connect the browser session and log in to x.com before any follow-graph writes","Run write commands interactively, never in page-less API mode","Add a browser-session preflight to batch unfollow scripts","Provision a logged-in Chrome profile for CI if writes are required there"],"tags":["twitter","browser-session","precondition","write-command"],"backgroundTag":"browser-session-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}