Skip to content

fix(extension): re-issue waitForSelector when a navigation destroys its context - #2983

Open
arrufat wants to merge 1 commit into
browserbase:mainfrom
arrufat:fix/wait-for-selector-across-navigation
Open

arrufat wants to merge 1 commit into
browserbase:mainfrom
arrufat:fix/wait-for-selector-across-navigation

Conversation

@arrufat

@arrufat arrufat commented Sep 18, 2026

Copy link
Copy Markdown

Fixes #2982.

Why

page.waitForSelector() is a single Runtime.evaluate (awaitPromise) pinned to the execution context that was current when it was issued. The typical caller issues it right after the locator.click() that submits a form, so the context is the document about to be navigated away from. When the commit lands after the evaluate was accepted, Chrome rejects it with -32000 Inspected target navigated or closed (V8 words it Execution context was destroyed). Frame.evaluateInLocatorWorld retries only Cannot find context with specified id, so the wait failed.

With a local form target that answers after 100 ms this fails deterministically on Chrome 153; with a fast target it fails about 2 runs in 5. The standalone repro is in #2982.

What changed

packages/extension/understudy/page.tswaitForSelector loops: it re-resolves its target frame (the root wrapper is replaced on root swaps), rebuilds the invocation with the time left, and re-issues the wait when the previous evaluate was rejected with one of the navigation-teardown messages. Anything else, and an exhausted budget, still throw the original error.

The retry stays scoped to this wait, which has no side effects. Frame.evaluate is unchanged, since replaying an arbitrary evaluate after it ran can duplicate side effects (#2751).

packages/extension/tests/page-wait-for-selector-navigation.test.ts — fake-session unit test: the outgoing world's evaluate rejects the way Chrome does while the new document's contexts arrive; the wait resolves from the new world, carrying the remaining timeout; an exhausted budget and an unrelated error are not retried.

Test plan

  • vitest run packages/extension/tests packages/extension/understudy: 375 passed (the 3 extension-build tests need a vite build I did not run locally)
  • tsc --noEmit -p packages/extension/tsconfig.json, oxfmt, oxlint
  • The same change on the v3 branch (packages/core/lib/v3/understudy/page.ts) passes a real-browser spec against Chrome 153 that fails 3/3 without it; backport PR follows

Summary by cubic

page.waitForSelector() now survives the navigation it is waiting through. When the navigation commit rejects the pending evaluate with "Inspected target navigated or closed" (or "Execution context was destroyed"), the wait is re-issued against the new document with the remaining timeout instead of failing. Fixes #2982.

Behavior

  • Retries only on navigation-teardown errors; other failures and an exhausted budget still throw the original error.
  • Re-resolves the target frame on each retry, since the root wrapper is replaced on root swaps.
  • Frame.evaluate is unchanged, since replaying arbitrary evaluates can duplicate side effects.

Testing

  • Adds a fake-session unit test covering the retry path, remaining-timeout carryover, exhausted budget, and unrelated errors.

Written for commit 681946f. Summary will update on new commits.

Review in cubic

…ts context

The wait is a single Runtime.evaluate pinned to the execution context that
was current when it was issued, typically the document a click is about to
navigate away from. When the commit lands after that, Chrome rejects the
pending evaluate with "Inspected target navigated or closed" (V8 words it
"Execution context was destroyed") and the wait failed, since only
"Cannot find context with specified id" was retried.

The wait has no side effects, so Page.waitForSelector now re-resolves its
target frame and re-issues the wait against the new document with the time
left. Other evaluate failures and an exhausted budget still throw.

Fixes browserbase#2982
@changeset-bot

changeset-bot Bot commented Sep 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 681946f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 18 packages
Name Type
@browserbasehq/stagehand-extension Patch
@browserbasehq/stagehand Patch
browse Patch
@browserbasehq/stagehand-integrations Patch
@browserbasehq/stagehand-integrations-example-eve-facade Patch
@browserbasehq/stagehand-integrations-example-pi-facade Patch
@browserbasehq/stagehand-integrations-claude-agent-sdk Patch
@browserbasehq/stagehand-integrations-example-claude-code-facade Patch
@browserbasehq/stagehand-integrations-codex-sdk Patch
@browserbasehq/stagehand-integrations-example-codex-facade Patch
@browserbasehq/stagehand-integrations-cursor-sdk Patch
@browserbasehq/stagehand-integrations-deepagents-sdk Patch
@browserbasehq/stagehand-integrations-eve-sdk Patch
@browserbasehq/stagehand-integrations-fx-sdk Patch
@browserbasehq/stagehand-integrations-mastra-sdk Patch
@browserbasehq/stagehand-integrations-example-mastra-facade Patch
@browserbasehq/stagehand-integrations-pi-sdk Patch
@browserbasehq/stagehand-integrations-example-vercel-ai-facade Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

This PR is from an external contributor and must be approved by a stagehand team member with write access before CI can run.
Approving the latest commit mirrors it into an internal PR owned by the approver.
If new commits are pushed later, the internal PR stays open but is marked stale until someone approves the latest external commit and refreshes it.

@github-actions github-actions Bot added external-contributor Tracks PRs mirrored from external contributor forks. external-contributor:awaiting-approval Waiting for a stagehand team member to approve the latest external commit. labels Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contributor:awaiting-approval Waiting for a stagehand team member to approve the latest external commit. external-contributor Tracks PRs mirrored from external contributor forks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

waitForSelector fails with "Inspected target navigated or closed" when the click-triggered navigation commits after the wait is issued

1 participant