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 IDs

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Call browser_tab_list to get currently valid page IDs and switch to one of them.
  2. If the tab should still exist, use a tool path that goes through ensurePage (lazy activation) rather than requirePage.
  3. 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

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


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