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
- Call listTabs() to get the current valid page IDs and retry with a fresh one.
- Take a new snapshot — the snapshot response carries current valid refs and tab IDs.
- Avoid caching pageIds across reconnects; re-list after onDisconnect.
- 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
- Re-list tabs after any onDisconnect/reconnect — tab IDs change.
- Do not cache pageIds across sessions.
- Call listTabs() before the first interaction of a turn to refresh the cache.
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
- Failed to create new tab
- agent-browser failed
- Invalid ${role}: argument cannot start with '-' (got: ${JSON
- agent-browser action failed
- No executable path for ${browser}
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/20b117ae97eacc5d.
Report an issue: GitHub.