{"record":{"id":"49cc50dd5e77c9ac","repo":"jackwener/OpenCLI","slug":"notebooklm-page-state-probe-returned-inconsistent","errorCode":null,"errorMessage":"NotebookLM page-state probe returned inconsistent URL and hostname fields","messagePattern":"NotebookLM page-state probe returned inconsistent URL and hostname fields","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/notebooklm/utils.js","lineNumber":681,"sourceCode":"    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'),\n        hostname: raw.hostname,\n        kind,\n        notebookId,\n        loginRequired: loginPath || raw.loginRequired,\n        notebookCount: Math.max(0, raw.notebookCount),\n    };\n    // Notebook pages can still contain \"sign in\" or login-related text fragments\n    // even when the active Google session is valid. Prefer the real page tokens\n    // as the stronger auth signal before declaring the session unauthenticated.\n    if (isNotebooklmHost(state.hostname) && state.loginRequired && !loginPath) {","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/notebooklm/utils.js#L663-L699","documentation":"After parsing the probe URL, getNotebooklmPageState cross-checks that parsed.hostname equals the separately reported raw.hostname field; a mismatch throws this CommandExecutionError. This consistency check catches probe data where url and hostname disagree — evidence the probe output was assembled incorrectly, intercepted, or spoofed, so the library refuses to trust it for URL classification.","triggerScenarios":"getNotebooklmPageState when the probe's hostname field differs from the hostname of raw.url — e.g. a redirect happened between the two reads, an extension modified one of them, or the probe script was altered/failed partially so stale or wrong values were captured.","commonSituations":"Mid-navigation race where location changed between field reads; security tooling or extensions rewriting location fields; a modified/custom probe returning inconsistent data; unusual redirects between google.com subdomains during login flows.","solutions":["Retry the probe after navigation settles (wait for load/idle) — races between reads are the usual cause","Reload the NotebookLM page and re-run getNotebooklmPageState","Disable browser extensions/proxies that rewrite location and retry","Update the library so url and hostname are captured atomically from the parsed URL rather than separately","Manually confirm the final URL in the browser to check for unexpected redirects"],"exampleFix":"// before: two independent reads in probe\nreturn { url: window.location.href, hostname: window.location.hostname, ... };\n// after: derive both from one value\nconst url = window.location.href;\nreturn { url, hostname: new URL(url).hostname, ... };","handlingStrategy":"validation","validationCode":"function hostnameMatches(state) {\n  try { return new URL(state.url).hostname === state.hostname; }\n  catch { return false; }\n}\n// after obtaining state, before trusting it:\nif (!hostnameMatches(state)) {\n  await page.reload();\n  // re-fetch state\n}","typeGuard":"function hasConsistentHost(v) {\n  if (typeof v?.url !== 'string' || typeof v?.hostname !== 'string') return false;\n  try { return new URL(v.url).hostname === v.hostname; } catch { return false; }\n}","tryCatchPattern":"try {\n  const state = await getNotebooklmPageState(page);\n} catch (e) {\n  if (!/inconsistent URL and hostname/.test(e.message)) throw e;\n  await page.waitForLoadState?.('load') ?? await page.wait(2); // let navigation settle\n  const state = await getNotebooklmPageState(page);\n}","preventionTips":["Capture url and hostname atomically (derive both from one parsed URL) if customizing the probe","Avoid probing mid-redirect or during login flows","Wait for navigation to complete before reading page state","Disable extensions/security tooling that rewrites location fields","Treat mismatches as a signal to reload and re-probe, not to proceed with partial data"],"tags":["notebooklm","page-state","url-parsing","consistency-check"],"backgroundTag":"page-state-probe-malformed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}