{"record":{"id":"374eb09d742b1429","repo":"jackwener/OpenCLI","slug":"notebooklm-page-state-probe-returned-malformed-bro","errorCode":null,"errorMessage":"NotebookLM page-state probe returned malformed Browser Bridge data","messagePattern":"NotebookLM page-state probe returned malformed Browser Bridge data","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/notebooklm/utils.js","lineNumber":671,"sourceCode":"    const textNodes = Array.from(document.querySelectorAll('a, button, [role=\"button\"], h1, h2'))\n      .map(node => (node.textContent || '').trim().toLowerCase())\n      .filter(Boolean);\n    const loginRequired = path === '/login' || path.startsWith('/login/') || textNodes.some(text =>\n      text.includes('sign in') ||\n      text.includes('log in') ||\n      text.includes('登录') ||\n      text.includes('登入')\n    );\n\n    const notebookCount = Array.from(document.querySelectorAll('a[href*=\"/notebook/\"]'))\n      .map(node => node instanceof HTMLAnchorElement ? node.href : '')\n      .filter(Boolean)\n      .reduce((count, href, index, list) => list.indexOf(href) === index ? count + 1 : count, 0);\n\n    return { url, title, hostname, kind, notebookId, loginRequired, notebookCount, path };\n  })()`, 'page-state probe');\n    if (!isPlainObject(raw) || typeof raw.url !== 'string' || typeof raw.title !== 'string' || typeof raw.hostname !== 'string' || typeof raw.kind !== 'string' || typeof raw.notebookId !== 'string' || typeof raw.loginRequired !== 'boolean' || typeof raw.notebookCount !== 'number' || !Number.isFinite(raw.notebookCount)) {\n        throw new CommandExecutionError('NotebookLM page-state probe returned malformed Browser Bridge data');\n    }\n    let parsed;\n    try {\n        parsed = new URL(raw.url);\n    }\n    catch {\n        throw new CommandExecutionError('NotebookLM page-state probe returned an invalid URL');\n    }\n    if (parsed.hostname !== raw.hostname) {\n        throw new CommandExecutionError('NotebookLM page-state probe returned inconsistent URL and hostname fields');\n    }\n    const trusted = parseTrustedNotebooklmUrl(parsed.href);\n    const kind = trusted ? classifyNotebooklmPage(trusted.href) : 'unknown';\n    const notebookId = kind === 'notebook' ? parseNotebooklmIdFromUrl(trusted.href) : '';\n    const loginPath = Boolean(trusted && (trusted.pathname === '/login' || trusted.pathname.startsWith('/login/')));\n    const state = {\n        url: raw.url,\n        title: normalizeNotebooklmTitle(raw?.title, 'NotebookLM'),","sourceCodeStart":653,"sourceCodeEnd":689,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/notebooklm/utils.js#L653-L689","documentation":"getNotebooklmPageState injects a probe script via the Browser Bridge that must return a plain object with typed fields (url, title, hostname, kind, notebookId, loginRequired, notebookCount). If the returned raw value is not an object or any field has the wrong type, this CommandExecutionError is thrown. It guards against the injected script failing, being blocked, or returning null/garbage so downstream URL classification never operates on bad data.","triggerScenarios":"Calling getNotebooklmPageState when the evaluated probe returns non-object data — the page is on a chrome error page or about:blank, content script evaluation is blocked (CSP/extension), the browser bridge returns undefined on failure, or NotebookLM's page structure changed so a probe variable is undefined.","commonSituations":"Browser on an error/crash page instead of NotebookLM; navigation interrupted mid-probe; Browser Bridge desync after tab crash; future NotebookLM redesign removing fields the probe reads; headless environment quirks.","solutions":["Reload the NotebookLM page and retry the state probe","Confirm the page is actually on notebooklm.google.com (check page.url()) before probing","Re-launch or re-attach the browser/Bridge if the tab crashed","Update the library if NotebookLM's page structure changed the probe inputs","Add a wait for page readiness (network idle / selector) before probing"],"exampleFix":"// before\nconst state = await getNotebooklmPageState(page);\n// after: pre-validate location and retry on malformed probe\nif (!page.url().includes('notebooklm.google.com')) await openNotebooklmHome(page);\nlet state;\ntry { state = await getNotebooklmPageState(page); }\ncatch (e) {\n  if (!/malformed Browser Bridge data/.test(e.message)) throw e;\n  await page.reload();\n  state = await getNotebooklmPageState(page);\n}","handlingStrategy":"type-guard","validationCode":"function isValidPageState(raw) {\n  return raw !== null && typeof raw === 'object'\n    && typeof raw.url === 'string'\n    && typeof raw.title === 'string'\n    && typeof raw.hostname === 'string'\n    && typeof raw.kind === 'string'\n    && typeof raw.notebookId === 'string'\n    && typeof raw.loginRequired === 'boolean'\n    && typeof raw.notebookCount === 'number'\n    && Number.isFinite(raw.notebookCount);\n}","typeGuard":"function isNotebooklmPageState(v) {\n  return v !== null && typeof v === 'object' && typeof v.url === 'string'\n    && typeof v.hostname === 'string' && typeof v.notebookCount === 'number'\n    && Number.isFinite(v.notebookCount) && typeof v.loginRequired === 'boolean';\n}","tryCatchPattern":"try {\n  const state = await getNotebooklmPageState(page);\n} catch (e) {\n  if (!/malformed Browser Bridge data/.test(e.message)) throw e;\n  await page.reload(); // recover from error page / interrupted navigation\n  const state = await getNotebooklmPageState(page);\n}","preventionTips":["Confirm page.url() is on notebooklm.google.com before probing","Wait for page readiness (selector/network idle) before running the probe","Reload after crashes or error pages — probes fail on synthetic pages","Keep the library updated for NotebookLM page-structure changes","Avoid probing during active navigation"],"tags":["notebooklm","page-state","browser-bridge","schema-validation"],"backgroundTag":"page-state-probe-malformed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}