n8n-io/n8n · error · PageNotFoundError

Page not found: ${pageId}

Error message

Page not found: ${pageId}

What it means

Thrown as PageNotFoundError by switchToTab when the requested pageId is not found in the tab cache even after a refreshTabs() re-query. PageNotFoundError extends McpBrowserError and carries a hint: 'The page may have been closed. List open pages with browser_tab_list.' The message interpolates the pageId that was not resolved.

Source

Thrown at packages/@n8n/mcp-browser/src/adapters/agent-browser.ts:123

			const data = response.data as { tabs?: TabData[] } | undefined;
			this.tabCache = data?.tabs ?? [];
			for (const tab of this.tabCache) {
				this.urlCache.set(tab.tabId, tab.url);
			}
		} catch (e) {
			log.debug('refreshTabs failed, assuming no active tabs available', { error: e });
			this.tabCache = [];
			this.urlCache.clear();
		}
		return this.tabCache;
	}

	private async switchToTab(pageId: string): Promise<void> {
		let tab = this.tabCache.find((t) => t.tabId === pageId);
		if (!tab) {
			await this.refreshTabs();
			tab = this.tabCache.find((t) => t.tabId === pageId);
			if (!tab) throw new PageNotFoundError(pageId);
		}
		if (tab.active) return;
		await this.run(['tab', tab.tabId]);
		for (const t of this.tabCache) t.active = t.tabId === pageId;
	}

	private resolveTarget(target: ElementTarget): string {
		const value =
			'ref' in target
				? target.ref.startsWith('@')
					? target.ref
					: `@${target.ref}`
				: target.selector;
		return AgentBrowserAdapter.assertSafeArg(value, 'element target');
	}

	private static assertSafeArg(value: string, role: string): string {
		if (value.length > 1 && value.startsWith('-')) {

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Call listTabs() to get the current valid page IDs and retry with a fresh one.
  2. Take a new snapshot — the snapshot response carries current valid refs and tab IDs.
  3. Avoid caching pageIds across reconnects; re-list after onDisconnect.
  4. If using the agent-browser adapter, remember refreshTabs only retries once before throwing.

Example fix

// before — reusing a stale pageId
await adapter.click(oldPageId, target);

// after — re-list on failure
try {
  await adapter.click(pageId, target);
} catch (err) {
  if (err instanceof PageNotFoundError) {
    const tabs = await adapter.listTabs();
    pageId = tabs[0].id;
    await adapter.click(pageId, target);
  } else throw err;
}
Defensive patterns

Strategy: try-catch

Type guard

import { PageNotFoundError } from '../errors';

function isPageNotFound(error: unknown): boolean {
  return error instanceof PageNotFoundError;
}

Try / catch

try {
  await adapter.click(pageId, target);
} catch (err) {
  if (err instanceof PageNotFoundError) {
    const tabs = await adapter.listTabs();
    pageId = tabs[0]?.id ?? pageId;
    await adapter.click(pageId, target);
  } else throw err;
}

Prevention

When it happens

Trigger: Any adapter method that calls switchToTab(pageId) — click, type, snapshot, navigate, scroll, etc. — when pageId does not match any tab in the refreshed tab list. Happens when the tab was closed in the browser, the pageId is stale from a previous session, or the pageId is wrong/typo'd.

Common situations: The browser tab was closed by the user or by closePage since the last snapshot; the caller reused a pageId from before a reconnect (tabCache was rebuilt with new IDs); agent-browser returned a different tabId format after a version change; concurrent callers raced and one closed the tab.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/20b117ae97eacc5d. Report an issue: GitHub.