{"record":{"id":"74cd7140ad11aa28","repo":"jackwener/OpenCLI","slug":"claude-requires-a-logged-in-browser-session","errorCode":null,"errorMessage":"Claude requires a logged-in browser session.","messagePattern":"Claude requires a logged-in browser session\\.","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/claude/utils.js","lineNumber":56,"sourceCode":"}\n\nexport async function getPageState(page) {\n    return page.evaluate(`(() => {\n        var composer = document.querySelector('${COMPOSER_SELECTOR}');\n        var userMenu = document.querySelector('[data-testid=\"user-menu-button\"]');\n        return {\n            url: window.location.href,\n            title: document.title,\n            hasComposer: !!composer,\n            isLoggedIn: !!userMenu,\n        };\n    })()`);\n}\n\nexport async function ensureClaudeLogin(page, message = 'Claude requires a logged-in browser session.') {\n    const state = await getPageState(page);\n    if (!state.isLoggedIn) {\n        throw new AuthRequiredError(CLAUDE_DOMAIN, message);\n    }\n    return state;\n}\n\nexport async function ensureClaudeComposer(page, message = 'Claude composer is not available on the current page.') {\n    const state = await ensureClaudeLogin(page, message);\n    if (!state.hasComposer) {\n        throw new CommandExecutionError(message);\n    }\n    return state;\n}\n\nexport function requireNonEmptyPrompt(prompt, commandName) {\n    const text = String(prompt ?? '').trim();\n    if (!text) {\n        throw new ArgumentError(\n            `${commandName} prompt cannot be empty`,\n            `Example: opencli ${commandName} \"hello\"`,","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/claude/utils.js#L38-L74","documentation":"This AuthRequiredError is thrown by ensureClaudeLogin when getPageState reports isLoggedIn === false for claude.ai — i.e. the automated browser has no valid logged-in session. All claude detail/history/read/send commands call it before scraping or interacting, so any page operation requires an authenticated session first. A caller-supplied message overrides the default text.","triggerScenarios":"Any claude command (detail, history, read, send) runs while the browser lacks a claude.ai login: cookies expired or were cleared, the user never completed `opencli claude auth`, Claude force-logged-out the session, or getPageState's login detection fails to recognize the logged-in UI after a Claude frontend change.","commonSituations":"sessionKey cookie expired (Claude sessions age out); cookies cleared by privacy tools or browser restart; user ran a claude command before ever running the auth flow; Claude invalidated the session server-side (password change, SSO expiry); library's login-detection selector broken by a Claude UI update.","solutions":["Run `opencli claude auth` (or `opencli claude whoami`) to launch the login flow and establish a fresh session before running detail/history/read/send","Log in manually in the automated browser window if the flow is running, completing any 2FA/SSO/email-code steps","Check that privacy extensions or browser settings are not stripping claude.ai cookies between commands","If you were logged in but it broke recently, verify claude.ai in a normal browser — a forced logout (password change) requires re-login","Update the library if login detection broke due to a Claude UI change"],"exampleFix":"// before\nopencli claude read   // no session -> AuthRequiredError\n// after\nopencli claude auth    # complete login in the automated browser\nopencli claude read","handlingStrategy":"validation","validationCode":"// verify a session exists before running any claude command\nconst cookies = await page.getCookies({ url: 'https://claude.ai' });\nif (!cookies.some(c => c.name === 'sessionKey' && c.value)) {\n  throw new Error('Not logged in — run `opencli claude auth` first');\n}","typeGuard":"function isClaudeLoggedIn(state) {\n  return !!state && state.isLoggedIn === true;\n}","tryCatchPattern":"try {\n  await ensureClaudeLogin(page);\n} catch (e) {\n  if (e instanceof AuthRequiredError || /logged-in browser session/.test(e.message)) {\n    await runClaudeAuthFlow();               // fresh login, then proceed\n    return ensureClaudeLogin(page);\n  }\n  throw e;\n}","preventionTips":["Always run `opencli claude auth` before detail/history/read/send","Handle AuthRequiredError centrally by triggering the login flow and retrying","Avoid privacy tools that clear claude.ai cookies between commands","Re-authenticate after password changes or SSO session expiry","Update the library if Claude's logged-in UI markers change"],"tags":["auth","claude","session","login-required"],"backgroundTag":"login-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}