{"record":{"id":"22995f85d05d7088","repo":"jackwener/OpenCLI","slug":"browser-session-required-for-twitter-unlike","errorCode":null,"errorMessage":"Browser session required for twitter unlike","messagePattern":"Browser session required for twitter unlike","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/unlike.js","lineNumber":19,"sourceCode":"import { CommandExecutionError, TimeoutError } from '@jackwener/opencli/errors';\nimport { cli, Strategy } from '@jackwener/opencli/registry';\nimport { parseTweetUrl, buildTwitterArticleScopeSource } from './shared.js';\n\ncli({\n    site: 'twitter',\n    name: 'unlike',\n    access: 'write',\n    description: 'Remove a like from a specific tweet',\n    domain: 'x.com',\n    strategy: Strategy.UI,\n    browser: true,\n    args: [\n        { name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to unlike' },\n    ],\n    columns: ['status', 'message'],\n    func: async (page, kwargs) => {\n        if (!page)\n            throw new CommandExecutionError('Browser session required for twitter unlike');\n        const target = parseTweetUrl(kwargs.url);\n        await page.goto(target.url);\n        await page.wait({ selector: '[data-testid=\"primaryColumn\"]' });\n        const result = await page.evaluate(`(async () => {\n        let writeStarted = false;\n        try {\n            ${buildTwitterArticleScopeSource(target.id)}\n            // Poll for the tweet to render. State probes scoped to the article\n            // matching the requested status id — bare querySelector on a\n            // conversation page would silently grab the first article (e.g.\n            // the parent tweet) and unlike the wrong one.\n            let attempts = 0;\n            let likeBtn = null;\n            let unlikeBtn = null;\n            let targetArticle = null;\n\n            while (attempts < 20) {\n                targetArticle = findTargetArticle();","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/unlike.js#L1-L37","documentation":"The `twitter unlike` command is declared with `browser: true` and Strategy.UI, so its func expects a live, logged-in browser `page`. The registry passes `page` as the first argument; when no browser session exists (e.g. running outside an interactive/browser-backed context or the session failed to start), `page` is falsy and this CommandExecutionError is thrown before any navigation happens. Nothing was touched on x.com — it is a precondition failure.","triggerScenarios":"Calling `opencli twitter unlike <url>` when the browser-backed session is unavailable: no browser launched/attached, running in a non-browser headless mode, or the browser process died and the registry passed `page === null/undefined` to func.","commonSituations":"Running the CLI in CI without a browser session; browser closed mid-session; invoking the command programmatically through the registry without initializing the browser; misconfigured opencli browser settings (e.g. missing browser binary or profile path).","solutions":["Start a browser session first (ensure opencli launches/attaches its browser and you are logged into x.com), then re-run `opencli twitter unlike <tweet-url>`.","If running in CI/headless, configure a browser-capable environment (install the browser and enable the browser strategy) instead of the default no-browser mode.","Check that the browser did not crash before the call — restart the CLI process to rebuild the session.","If embedding the registry, verify you pass a valid page/session to the command invocation rather than undefined."],"exampleFix":"// before\nopencli twitter unlike https://x.com/user/status/123  // no browser session -> error\n// after\nopencli browser start           # or open the interactive session first\nopencli twitter unlike https://x.com/user/status/123","handlingStrategy":"validation","validationCode":"// before invoking: ensure a browser-backed session exists\nconst page = await session.getPage(); // or registry-provided accessor\nif (!page) {\n  await startBrowserSession(); // launch/attach browser and log into x.com\n}\nreturn runCommand('twitter unlike', { url: tweetUrl });","typeGuard":"function hasBrowserPage(page) {\n  return typeof page === 'object' && page !== null &&\n    typeof page.goto === 'function' && typeof page.evaluate === 'function';\n}","tryCatchPattern":"try {\n  await cli.run(['twitter', 'unlike', tweetUrl]);\n} catch (e) {\n  if (/Browser session required/.test(e.message)) {\n    await startBrowserSession();\n    await cli.run(['twitter', 'unlike', tweetUrl]);\n  } else throw e;\n}","preventionTips":["Always start/attach the browser session before any browser:true twitter command.","In CI, explicitly configure a headless browser instead of the default no-browser mode.","Health-check the session (page.evaluate of a trivial expression) before write commands.","Wrap browser-dependent commands behind a helper that lazily starts the session."],"tags":["browser","precondition","twitter","session"],"backgroundTag":"browser-session-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}