n8n-io/n8n · error · PageNotFoundError
Page not found: ${pageId}
Error message
Page not found: ${pageId} What it means
PageNotFoundError is thrown by requirePage when pageStates.get(pageId) returns undefined — the adapter has no tracked Playwright PageState for the given ID. This is the direct-lookup path: the page was never activated or was already removed from internal tracking.
Source
Thrown at packages/@n8n/mcp-browser/src/adapters/playwright.ts:905
if (error instanceof StaleRefError) throw error;
throw new StaleRefError(ref);
}
return locator;
}
// =========================================================================
// Private helpers
// =========================================================================
private requireContext(): BrowserContext {
if (!this.context) throw new Error('Browser context not initialized');
return this.context;
}
private requirePage(pageId: string): PageState {
const state = this.pageStates.get(pageId);
if (!state) throw new PageNotFoundError(pageId);
return state;
}
/**
* Lazy page activation: return the page if already tracked, otherwise
* tell the relay to activate the tab (attach debugger + emit
* Target.attachedToTarget) and wait for Playwright to create the Page.
*/
private async ensurePage(pageId: string): Promise<PageState> {
const existing = this.pageStates.get(pageId);
if (existing) {
log.debug('ensurePage: page already tracked:', pageId);
return existing;
}
if (!this.relay || !this.context) throw new PageNotFoundError(pageId);
// Guard: don't attempt lazy activation for unknown/empty tab IDsView on GitHub (pinned to 5ac6606e81)
Solutions
- Call browser_tab_list to get currently valid page IDs and switch to one of them.
- If the tab should still exist, use a tool path that goes through ensurePage (lazy activation) rather than requirePage.
- Re-snapshot after reconnecting to refresh all page IDs.
Example fix
// before — pageId no longer tracked
const state = adapter.requirePage('page-3'); // throws PageNotFoundError
// after — list valid pages first
const pages = await connection.getConnection().adapter.listTabs();
const validId = pages[0].id; Defensive patterns
Strategy: validation
Validate before calling
const pages = await adapter.listTabs();
const validIds = new Set(pages.map((p) => p.id));
if (!validIds.has(pageId)) { pageId = pages[0]?.id; } Type guard
function isKnownPageId(known: Set<string>, id: string): boolean {
return known.has(id);
} Try / catch
try {
return adapter.requirePage(pageId);
} catch (e) {
if (e instanceof PageNotFoundError) {
const tabs = await adapter.listTabs();
return adapter.ensurePage(tabs[0].id);
}
throw e;
} Prevention
- Refresh the known-pageId set after every connect/disconnect cycle.
- Do not cache pageIds across sessions.
- Prefer ensurePage (lazy activation) over requirePage for externally-sourced IDs.
When it happens
Trigger: Calling requirePage (used internally by tools that need an already-active page) with a pageId that was never activated via ensurePage, or whose PageState was cleaned up after the page closed.
Common situations: Using a pageId from a previous connection session after a disconnect/reconnect. Operating on a tab that was closed by the user or by a closeTab call. Typo or stale cached pageId in agent state.
Related errors
- No executable path for ${browser}
- No file chooser pending and no element target provided
- Stale element ref: ${ref}
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/f990c65e9825fafc.
Report an issue: GitHub.