jackwener/OpenCLI · error · CommandExecutionError

NotebookLM home-link probe returned an untrusted or…

Error message

NotebookLM home-link probe returned an untrusted or mismatched notebook URL

What it means

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.

Solutions

  1. Reload the NotebookLM home page and retry so anchors resolve to real notebook hrefs.
  2. Update the CLI so its link extractor filters out non-notebook anchors (buttons, section links).
  3. Verify with your browser that the listed notebooks open at notebooklm.google.com/notebook/<id> URLs.
  4. If scraping custom markup, ensure hrefs are resolved to absolute trusted URLs before validation.

Example fix

// before: emitting every scraped link
rows.forEach(r => out.push({ id: r.id, url: r.url }));

// after: only trusted notebook URLs
for (const r of rows) {
  const url = normalizeNotebooklmNotebookUrl(r.url, r.id);
  if (url) out.push({ id: r.id, url });
}
Defensive patterns

Strategy: validation

Validate before calling

for (const r of rows) {
  if (!/^https:\/\/notebooklm\.google\.com\/notebook\//.test(r.url) || !r.url.includes(r.id)) {
    console.warn(`Skipping untrusted notebook link for id ${r.id}`);
  }
}

Type guard

const isTrustedRowUrl = (r) => typeof r?.url === 'string' && r.url.startsWith('https://notebooklm.google.com/notebook/') && normalizeNotebooklmNotebookUrl(r.url, r.id) !== null;

Try / catch

try {
  rows = await probeHomeLinks();
} catch (e) {
  if (String(e.message).includes('untrusted or mismatched notebook URL')) {
    console.error('Reload the NotebookLM home page so links resolve to real notebook URLs.');
  } else { throw e; }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/b00d67c6e4687c08. Report an issue: GitHub.

Appendix: source

Thrown at clis/notebooklm/utils.js:801

        title,
        url: href,
        source: 'home-links',
        is_owner: true,
        created_at: createdAtHint || null,
      });
    }

    return rows;
  })()`, 'home-link probe');
    if (!Array.isArray(raw))
        throw new CommandExecutionError('NotebookLM home-link probe returned malformed Browser Bridge data');
    return raw.map((row) => {
        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')) {
            throw new CommandExecutionError('NotebookLM home-link probe returned a malformed row');
        }
        const url = normalizeNotebooklmNotebookUrl(row.url, row.id);
        if (!url) {
            throw new CommandExecutionError('NotebookLM home-link probe returned an untrusted or mismatched notebook URL');
        }
        return {
        id: row.id,
        title: normalizeNotebooklmTitle(row.title, 'Untitled Notebook'),
        url,
        source: 'home-links',
        is_owner: row.is_owner === false ? false : true,
        created_at: normalizeNotebooklmCreatedAt(row.created_at),
        };
    });
}
export async function listNotebooklmSourcesFromPage(page) {
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    const notebookMatch = window.location.href.match(/\\/notebook\\/([^/?#]+)/);
    const notebookId = notebookMatch ? notebookMatch[1] : '';
    if (!notebookId) return [];

    const skip = new Set([

View on GitHub (pinned to 49907e53dc)