{"record":{"id":"e458889a1fcbaf4c","repo":"jackwener/OpenCLI","slug":"notebooklm-tokens","errorCode":"NOTEBOOKLM_TOKENS","errorMessage":"NOTEBOOKLM_TOKENS","messagePattern":"NOTEBOOKLM_TOKENS","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/notebooklm/rpc.js","lineNumber":32,"sourceCode":"    if (error instanceof CliError)\n        throw error;\n    throw new CommandExecutionError(`NotebookLM ${label} failed: ${error?.message || error}`);\n}\n\nexport function unwrapNotebooklmEvaluateResult(payload) {\n    if (payload && typeof payload === 'object' && !Array.isArray(payload) && 'session' in payload && 'data' in payload) {\n        return payload.data;\n    }\n    return payload;\n}\n\nexport function extractNotebooklmPageAuthFromHtml(html, sourcePath = '/', preferredTokens) {\n    const csrfMatch = html.match(/\"SNlM0e\":\"([^\"]+)\"/);\n    const sessionMatch = html.match(/\"FdrFJe\":\"([^\"]+)\"/);\n    const csrfToken = preferredTokens?.csrfToken?.trim() || (csrfMatch ? csrfMatch[1] : '');\n    const sessionId = preferredTokens?.sessionId?.trim() || (sessionMatch ? sessionMatch[1] : '');\n    if (!csrfToken || !sessionId) {\n        throw new CliError('NOTEBOOKLM_TOKENS', 'NotebookLM page tokens were not found in the current page HTML', 'Open the NotebookLM notebook page in Chrome, wait for it to finish loading, then retry with --verbose if it still fails.');\n    }\n    return { csrfToken, sessionId, sourcePath: sourcePath || '/', authuser: preferredTokens?.authuser ?? '' };\n}\nasync function probeNotebooklmPageAuth(page) {\n    let evaluated;\n    try {\n        evaluated = await page.evaluate(`(() => {\n    const wiz = window.WIZ_global_data || {};\n    const html = document.documentElement.innerHTML;\n    const authMatch = (location.search || '').match(/[?&]authuser=(\\\\d+)/);\n    const pathMatch = (location.pathname || '').match(/^\\\\/u\\\\/(\\\\d+)\\\\//);\n    return {\n      html,\n      sourcePath: location.pathname || '/',\n      readyState: document.readyState || '',\n      csrfToken: typeof wiz.SNlM0e === 'string' ? wiz.SNlM0e : '',\n      sessionId: typeof wiz.FdrFJe === 'string' ? wiz.FdrFJe : '',\n      authuser: authMatch ? authMatch[1] : (pathMatch ? pathMatch[1] : ''),","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/notebooklm/rpc.js#L14-L50","documentation":"extractNotebooklmPageAuthFromHtml scrapes the CSRF token (SNlM0e) and session id (FdrFJe) out of the served NotebookLM page HTML. If neither the page HTML nor preferredTokens yields a non-empty value for both, it throws CliError with code NOTEBOOKLM_TOKENS. The library refuses to sign batchexecute RPCs without these tokens because every call would fail server-side.","triggerScenarios":"extractNotebooklmPageAuthFromHtml receives HTML without \"SNlM0e\":\"...\" or \"FdrFJe\":\"...\" and preferredTokens lacks csrfToken or sessionId (empty or whitespace-only).","commonSituations":"Page captured is a Google login/consent interstitial instead of the notebook app; NotebookLM served an error page; NotebookLM renamed its internal token keys in a frontend update; --authuser/--hl query params landed on a variant page missing tokens.","solutions":["Open the actual NotebookLM notebook page in Chrome, wait for it to finish loading, then retry","Retry with --verbose to dump the HTML and confirm which token is missing","Pass --authuser matching the logged-in profile so the app page (not a redirect) is served","Update the CLI if NotebookLM changed its internal token serialization"],"exampleFix":"// before (parsing a wrong page)\nconst html = await page.content(); // login interstitial\nconst tokens = extractNotebooklmPageAuthFromHtml(html);\n// after (verify you are on the app page first)\nconst html = await page.content();\nif (!/\"SNlM0e\":\"[^\"]+\"/.test(html)) throw new Error('not on NotebookLM app page; complete login first');\nconst tokens = extractNotebooklmPageAuthFromHtml(html);","handlingStrategy":"validation","validationCode":"function hasNotebooklmTokens(html) {\n  return /\"SNlM0e\":\"[^\"]+\"/.test(html) && /\"FdrFJe\":\"[^\"]+\"/.test(html);\n}\n// call before extractNotebooklmPageAuthFromHtml\nif (!hasNotebooklmTokens(html)) throw new Error('page HTML lacks NotebookLM tokens; open the notebook page fully loaded');","typeGuard":"function isTokenSet(t) {\n  return typeof t?.csrfToken === 'string' && t.csrfToken.length > 0 &&\n         typeof t?.sessionId === 'string' && t.sessionId.length > 0;\n}","tryCatchPattern":"try {\n  const tokens = extractNotebooklmPageAuthFromHtml(html);\n} catch (e) {\n  if (e.code === 'NOTEBOOKLM_TOKENS') {\n    console.error('Open the NotebookLM notebook page in Chrome, wait for full load, then retry.');\n  } else throw e;\n}","preventionTips":["Always run commands against a fully loaded NotebookLM notebook page, never a login or consent page","Match --authuser to the logged-in profile","Check tokens exist with debug logging after any NotebookLM frontend update"],"tags":["auth","csrf-token","scraping","notebooklm"],"backgroundTag":"csrf-token-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}