{"record":{"id":"a2217b23560fcf87","repo":"jackwener/OpenCLI","slug":"not-logged-in-sapisid-cookie-missing-a2217b","errorCode":null,"errorMessage":"Not logged in (SAPISID cookie missing)","messagePattern":"Not logged in \\(SAPISID cookie missing\\)","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"critical","filePath":"clis/youtube/unsubscribe.js","lineNumber":25,"sourceCode":"\ncli({\n    site: 'youtube',\n    name: 'unsubscribe',\n    access: 'write',\n    description: 'Unsubscribe from a YouTube channel',\n    domain: 'www.youtube.com',\n    strategy: Strategy.COOKIE,\n    args: [\n        { name: 'channel', required: true, positional: true, help: 'Channel ID (UCxxxx) or handle (@name)' },\n    ],\n    columns: ['status', 'message'],\n    func: async (page, kwargs) => {\n        const channelInput = String(kwargs.channel);\n        await prepareYoutubeApiPage(page);\n        // Read SAPISID directly from the cookie store via CDP — zero document.cookie round-trip\n        const sapisid = await readYoutubeSapisid(page);\n        if (!sapisid)\n            throw new AuthRequiredError('www.youtube.com', 'Not logged in (SAPISID cookie missing)');\n        const result = await page.evaluate(`\n      (async () => {\n        ${SAPISID_HASH_FN}\n\n        const cfg = window.ytcfg?.data_ || {};\n        const apiKey = cfg.INNERTUBE_API_KEY;\n        const context = cfg.INNERTUBE_CONTEXT;\n        if (!apiKey || !context) return { error: 'config', message: 'YouTube config not found' };\n\n        const authHash = await getSapisidHash(${JSON.stringify(sapisid)}, 'https://www.youtube.com');\n        if (!authHash) return { error: 'auth', message: 'Not logged in (SAPISID cookie missing)' };\n\n        ${RESOLVE_CHANNEL_HANDLE_FN}\n\n        let channelId = ${JSON.stringify(channelInput)};\n        channelId = await resolveChannelHandle(channelId, apiKey, context);\n\n        if (!channelId.startsWith('UC')) {","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/youtube/unsubscribe.js#L7-L43","documentation":"AuthRequiredError thrown before the unsubscribe command runs because no SAPISID cookie exists for www.youtube.com. The SAPISID cookie is only present when the browser profile is signed into YouTube/Google, and it is required to construct the SAPISIDHASH Authorization header for the subscription-removal internal API. The library fails fast so the caller can trigger a login flow.","triggerScenarios":"Calling the youtube unsubscribe command when readYoutubeSapisid(page) finds no SAPISID cookie — profile not logged in, cookies cleared, or session expired.","commonSituations":"Fresh/headless browser profile never signed into Google; cookies purged between runs; Google signed the account out; using the wrong user-data-dir that lacks YouTube session cookies.","solutions":["Log into www.youtube.com in the automation browser profile (manually or via a persisted, logged-in user-data-dir).","Verify the SAPISID cookie exists for www.youtube.com (e.g. via CDP cookie store) before running the command.","Use a persistent browser profile that retains valid Google session cookies instead of an ephemeral context.","Re-authenticate the Google account if the session was invalidated, then retry."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const sapisid = await readYoutubeSapisid(page);\nif (!sapisid) throw new Error('YouTube login required: SAPISID cookie missing');","typeGuard":null,"tryCatchPattern":"try {\n  await unsubscribe({ channel });\n} catch (e) {\n  if (e.name === 'AuthRequiredError') {\n    await loginToYoutube(browserProfile);\n    return unsubscribe({ channel });\n  }\n  throw e;\n}","preventionTips":["Use a persistent logged-in browser profile for subscription management commands.","Pre-flight verify the SAPISID cookie via CDP before running the command.","Detect AuthRequiredError and route to a login/re-auth flow instead of blind retries.","Avoid concurrent logins on the same Google account that invalidate each other's sessions."],"tags":["auth","youtube","cookies","login-required"],"backgroundTag":"sapisid-cookie-missing","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}