{"record":{"id":"b00d67c6e4687c08","repo":"jackwener/OpenCLI","slug":"notebooklm-home-link-probe-returned-an-untrusted-o","errorCode":null,"errorMessage":"NotebookLM home-link probe returned an untrusted or mismatched notebook URL","messagePattern":"NotebookLM home-link probe returned an untrusted or mismatched notebook URL","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/notebooklm/utils.js","lineNumber":801,"sourceCode":"        title,\n        url: href,\n        source: 'home-links',\n        is_owner: true,\n        created_at: createdAtHint || null,\n      });\n    }\n\n    return rows;\n  })()`, 'home-link probe');\n    if (!Array.isArray(raw))\n        throw new CommandExecutionError('NotebookLM home-link probe returned malformed Browser Bridge data');\n    return raw.map((row) => {\n        if (!isPlainObject(row) || typeof row.id !== 'string' || typeof row.title !== 'string' || typeof row.url !== 'string' || (row.created_at !== null && row.created_at !== undefined && typeof row.created_at !== 'string') || (row.is_owner !== undefined && typeof row.is_owner !== 'boolean')) {\n            throw new CommandExecutionError('NotebookLM home-link probe returned a malformed row');\n        }\n        const url = normalizeNotebooklmNotebookUrl(row.url, row.id);\n        if (!url) {\n            throw new CommandExecutionError('NotebookLM home-link probe returned an untrusted or mismatched notebook URL');\n        }\n        return {\n        id: row.id,\n        title: normalizeNotebooklmTitle(row.title, 'Untitled Notebook'),\n        url,\n        source: 'home-links',\n        is_owner: row.is_owner === false ? false : true,\n        created_at: normalizeNotebooklmCreatedAt(row.created_at),\n        };\n    });\n}\nexport async function listNotebooklmSourcesFromPage(page) {\n    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {\n    const notebookMatch = window.location.href.match(/\\\\/notebook\\\\/([^/?#]+)/);\n    const notebookId = notebookMatch ? notebookMatch[1] : '';\n    if (!notebookId) return [];\n\n    const skip = new Set([","sourceCodeStart":783,"sourceCodeEnd":819,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/notebooklm/utils.js#L783-L819","documentation":"After a home-link row passes shape validation, its url/id pair is normalized via normalizeNotebooklmNotebookUrl, which only accepts trusted NotebookLM notebook URLs with a matching id. If normalization yields nothing, the row's link is untrusted (wrong host/path) or inconsistent with its id, so the CLI throws rather than emitting a bad notebook link.","triggerScenarios":"row.url points outside notebooklm.google.com, is a relative or javascript: link, encodes a different notebook id than row.id, or is empty while the row otherwise validated.","commonSituations":"Anchor elements on the home page that are not actual notebook links (menu buttons, 'New notebook' button) being scraped as rows, or SPA-router hrefs captured mid-navigation.","solutions":["Reload the NotebookLM home page and retry so anchors resolve to real notebook hrefs.","Update the CLI so its link extractor filters out non-notebook anchors (buttons, section links).","Verify with your browser that the listed notebooks open at notebooklm.google.com/notebook/<id> URLs.","If scraping custom markup, ensure hrefs are resolved to absolute trusted URLs before validation."],"exampleFix":"// before: emitting every scraped link\nrows.forEach(r => out.push({ id: r.id, url: r.url }));\n\n// after: only trusted notebook URLs\nfor (const r of rows) {\n  const url = normalizeNotebooklmNotebookUrl(r.url, r.id);\n  if (url) out.push({ id: r.id, url });\n}","handlingStrategy":"validation","validationCode":"for (const r of rows) {\n  if (!/^https:\\/\\/notebooklm\\.google\\.com\\/notebook\\//.test(r.url) || !r.url.includes(r.id)) {\n    console.warn(`Skipping untrusted notebook link for id ${r.id}`);\n  }\n}","typeGuard":"const isTrustedRowUrl = (r) => typeof r?.url === 'string' && r.url.startsWith('https://notebooklm.google.com/notebook/') && normalizeNotebooklmNotebookUrl(r.url, r.id) !== null;","tryCatchPattern":"try {\n  rows = await probeHomeLinks();\n} catch (e) {\n  if (String(e.message).includes('untrusted or mismatched notebook URL')) {\n    console.error('Reload the NotebookLM home page so links resolve to real notebook URLs.');\n  } else { throw e; }\n}","preventionTips":["Only trust absolute https notebooklm.google.com/notebook/<id> URLs.","Reload the page if you recently navigated so hrefs are not mid-transition.","Filter out non-notebook anchors (buttons, headers) before validating rows.","Never persist probe URLs without normalizing them first."],"tags":["notebooklm","url-validation","untrusted-url","probe"],"backgroundTag":"untrusted-url-rejected","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}