jackwener/OpenCLI · error · CliError
NOTEBOOKLM_TOKENS
NOTEBOOKLM_TOKENS
Error message
NOTEBOOKLM_TOKENS
What it means
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.
Source
Thrown at clis/notebooklm/rpc.js:32
if (error instanceof CliError)
throw error;
throw new CommandExecutionError(`NotebookLM ${label} failed: ${error?.message || error}`);
}
export function unwrapNotebooklmEvaluateResult(payload) {
if (payload && typeof payload === 'object' && !Array.isArray(payload) && 'session' in payload && 'data' in payload) {
return payload.data;
}
return payload;
}
export function extractNotebooklmPageAuthFromHtml(html, sourcePath = '/', preferredTokens) {
const csrfMatch = html.match(/"SNlM0e":"([^"]+)"/);
const sessionMatch = html.match(/"FdrFJe":"([^"]+)"/);
const csrfToken = preferredTokens?.csrfToken?.trim() || (csrfMatch ? csrfMatch[1] : '');
const sessionId = preferredTokens?.sessionId?.trim() || (sessionMatch ? sessionMatch[1] : '');
if (!csrfToken || !sessionId) {
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.');
}
return { csrfToken, sessionId, sourcePath: sourcePath || '/', authuser: preferredTokens?.authuser ?? '' };
}
async function probeNotebooklmPageAuth(page) {
let evaluated;
try {
evaluated = await page.evaluate(`(() => {
const wiz = window.WIZ_global_data || {};
const html = document.documentElement.innerHTML;
const authMatch = (location.search || '').match(/[?&]authuser=(\\d+)/);
const pathMatch = (location.pathname || '').match(/^\\/u\\/(\\d+)\\//);
return {
html,
sourcePath: location.pathname || '/',
readyState: document.readyState || '',
csrfToken: typeof wiz.SNlM0e === 'string' ? wiz.SNlM0e : '',
sessionId: typeof wiz.FdrFJe === 'string' ? wiz.FdrFJe : '',
authuser: authMatch ? authMatch[1] : (pathMatch ? pathMatch[1] : ''),View on GitHub (pinned to 49907e53dc)
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
Example fix
// before (parsing a wrong page)
const html = await page.content(); // login interstitial
const tokens = extractNotebooklmPageAuthFromHtml(html);
// after (verify you are on the app page first)
const html = await page.content();
if (!/"SNlM0e":"[^"]+"/.test(html)) throw new Error('not on NotebookLM app page; complete login first');
const tokens = extractNotebooklmPageAuthFromHtml(html); Defensive patterns
Strategy: validation
Validate before calling
function hasNotebooklmTokens(html) {
return /"SNlM0e":"[^"]+"/.test(html) && /"FdrFJe":"[^"]+"/.test(html);
}
// call before extractNotebooklmPageAuthFromHtml
if (!hasNotebooklmTokens(html)) throw new Error('page HTML lacks NotebookLM tokens; open the notebook page fully loaded'); Type guard
function isTokenSet(t) {
return typeof t?.csrfToken === 'string' && t.csrfToken.length > 0 &&
typeof t?.sessionId === 'string' && t.sessionId.length > 0;
} Try / catch
try {
const tokens = extractNotebooklmPageAuthFromHtml(html);
} catch (e) {
if (e.code === 'NOTEBOOKLM_TOKENS') {
console.error('Open the NotebookLM notebook page in Chrome, wait for full load, then retry.');
} else throw e;
} Prevention
- 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
When it happens
Trigger: extractNotebooklmPageAuthFromHtml receives HTML without "SNlM0e":"..." or "FdrFJe":"..." and preferredTokens lacks csrfToken or sessionId (empty or whitespace-only).
Common situations: 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.
Related errors
- hotels.ctrip.com
- vacations.ctrip.com
- guazi ${contextHint} hit an anti-bot challenge — Guazi may h
- No chats visible in sidebar. Are you logged in?
- Google SSO cookies (SID + SAPISID) missing
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e458889a1fcbaf4c.
Report an issue: GitHub.