microsoft/playwright · error · Error

Ref ${param.target} not found in the current page snapshot.

Error message

Ref ${param.target} not found in the current page snapshot. Try capturing new snapshot.

What it means

Thrown by Tab.targetLocator when params.target DOES match the aria-ref pattern (/^(f\d+)e\d+$/), so it is resolved via page.locator('aria-ref=...').normalize(), and that resolution throws (stale or unknown ref). The catch rewrites any error into the snapshot-stale message.

Source

Thrown at packages/playwright-core/src/tools/backend/tab.ts:515

  async targetLocators(params: { element?: string, target: string }[]): Promise<{ locator: playwright.Locator, resolved: string, selector: string }[]> {
    await this._initializedPromise;
    return Promise.all(params.map(async param => {
      if (!param.target.match(/^(f\d+)?e\d+$/)) {
        const selector = locatorOrSelectorAsSelector('javascript', param.target, this.context.config.testIdAttribute || 'data-testid');
        const handle = await this.page.$(selector);
        if (!handle)
          throw new Error(`"${param.target}" does not match any elements.`);
        handle.dispose().catch(() => {});
        return { locator: this.page.locator(selector), resolved: asLocator('javascript', selector), selector };
      } else {
        try {
          let locator = this.page.locator(`aria-ref=${param.target}`);
          if (param.element)
            locator = locator.describe(param.element);
          const resolved = await locator.normalize();
          return { locator, resolved: resolved.toString(), selector: locatorSelector(resolved) };
        } catch (e) {
          throw new Error(`Ref ${param.target} not found in the current page snapshot. Try capturing new snapshot.`);
        }
      }
    }));
  }

  async waitForTimeout(time: number) {
    if (this._javaScriptBlocked()) {
      await new Promise(f => setTimeout(f, time));
      return;
    }

    await this.page.evaluate(ms => new Promise(f => setTimeout(f, ms)), time).catch(() => {});
  }
}

export type ConsoleMessage = {
  type: ReturnType<playwright.ConsoleMessage['type']>;
  timestamp: number;

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Re-invoke browser_snapshot to obtain fresh refs, then retry with the new ref.
  2. Avoid caching refs across navigations or significant DOM mutations.
  3. If the element is stable, switch to a robust selector instead of a snapshot ref.

Example fix

// before
// snapshot taken 5 turns ago returned ref 'e7'; DOM since changed
await client.callTool('browser_click', { element: 'submit', target: 'e7' }); // throws

// after
await client.callTool('browser_snapshot', {});
// new ref 'e9'
await client.callTool('browser_click', { element: 'submit', target: 'e9' });
Defensive patterns

Strategy: retry

Validate before calling

function looksLikeRef(target: string): boolean {
  return /^(f\d+)?e\d+$/.test(target);
}
function refAge(snapshotTurn: number, currentTurn: number): number {
  return currentTurn - snapshotTurn;
}

Type guard

function isStaleRefError(e: unknown): boolean {
  return e instanceof Error && /Ref .* not found in the current page snapshot/.test(e.message);
}

Try / catch

for (let attempt = 0; attempt < 2; attempt++) {
  try {
    await client.callTool('browser_click', { element, target: ref });
    break;
  } catch (e) {
    if (isStaleRefError(e) && attempt === 0) {
      await client.callTool('browser_snapshot', {});
      // update `ref` from the new snapshot; continue
    } else throw e;
  }
}

Prevention

When it happens

Trigger: Passing an element ref (e.g. 'e7', 'f2e3') that was issued by an earlier browser_snapshot but is no longer valid — the page has changed, navigated, or the ref was from a different frame tree.

Common situations: Agent reusing refs across a navigation; long-running session where the snapshot expired; ref copied from a previous turn's snapshot after the DOM mutated.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/84dd7e3095253f72. Report an issue: GitHub.