{"record":{"id":"f990c65e9825fafc","repo":"n8n-io/n8n","slug":"page-not-found-pageid-f990c6","errorCode":null,"errorMessage":"Page not found: ${pageId}","messagePattern":"Page not found: (.+?)","errorType":"exception","errorClass":"PageNotFoundError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/mcp-browser/src/adapters/playwright.ts","lineNumber":905,"sourceCode":"\t\t\tif (error instanceof StaleRefError) throw error;\n\t\t\tthrow new StaleRefError(ref);\n\t\t}\n\n\t\treturn locator;\n\t}\n\n\t// =========================================================================\n\t// Private helpers\n\t// =========================================================================\n\n\tprivate requireContext(): BrowserContext {\n\t\tif (!this.context) throw new Error('Browser context not initialized');\n\t\treturn this.context;\n\t}\n\n\tprivate requirePage(pageId: string): PageState {\n\t\tconst state = this.pageStates.get(pageId);\n\t\tif (!state) throw new PageNotFoundError(pageId);\n\t\treturn state;\n\t}\n\n\t/**\n\t * Lazy page activation: return the page if already tracked, otherwise\n\t * tell the relay to activate the tab (attach debugger + emit\n\t * Target.attachedToTarget) and wait for Playwright to create the Page.\n\t */\n\tprivate async ensurePage(pageId: string): Promise<PageState> {\n\t\tconst existing = this.pageStates.get(pageId);\n\t\tif (existing) {\n\t\t\tlog.debug('ensurePage: page already tracked:', pageId);\n\t\t\treturn existing;\n\t\t}\n\n\t\tif (!this.relay || !this.context) throw new PageNotFoundError(pageId);\n\n\t\t// Guard: don't attempt lazy activation for unknown/empty tab IDs","sourceCodeStart":887,"sourceCodeEnd":923,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/mcp-browser/src/adapters/playwright.ts#L887-L923","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before — pageId no longer tracked\nconst state = adapter.requirePage('page-3'); // throws PageNotFoundError\n\n// after — list valid pages first\nconst pages = await connection.getConnection().adapter.listTabs();\nconst validId = pages[0].id;","handlingStrategy":"validation","validationCode":"const pages = await adapter.listTabs();\nconst validIds = new Set(pages.map((p) => p.id));\nif (!validIds.has(pageId)) { pageId = pages[0]?.id; }","typeGuard":"function isKnownPageId(known: Set<string>, id: string): boolean {\n  return known.has(id);\n}","tryCatchPattern":"try {\n  return adapter.requirePage(pageId);\n} catch (e) {\n  if (e instanceof PageNotFoundError) {\n    const tabs = await adapter.listTabs();\n    return adapter.ensurePage(tabs[0].id);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["page-not-found","playwright","tab-tracking"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}