# why
`waitForSelector()` did not include frame resolution or locator world
readiness in its timeout. time spent preparing or retrying the browser
wait was not deducted from its timeout. the browser helper also turned
zero into a fresh 30-second wait, & could keep watching the page after
the caller timed out.
# what changed
- gave selector waits one shared deadline across frame resolution,
locator-world readiness, & the element wait. internal calls reuse their
parent's remaining time without taking ownership of its progress.
- calculated the browser timeout immediately before dispatch, including
retries after a lost execution context. expired calls cannot start
another wait.
- kept the 30-second default & made explicit `0` unlimited throughout
the wait.
- gave each browser wait a disposable handle. completion, timeout, &
failure release its observers, timers, DOM-ready listener, & remote
reference. handles returned after expiry are cleaned up too; concurrent
waits stay independent.
- bounded cleanup & kept it in the original context. cleanup failure
cannot replace the operation's error or trigger a new locator-world
lookup.
- fixed observer setup before the document root exists & prevented
queued callbacks from installing observers after settlement
# test plan
- `understudy/waitForSelector.test.ts` checks the default, finite, &
unlimited deadlines, inherited progress ownership, & time consumed by
resolution, readiness, & context recovery. it also covers expiry before
dispatch, late installation & results, failed or stalled cleanup,
session closure, & separate handles for concurrent waits.
- `tests/wait-for-selector.test.ts` checks all four element states,
finite expiry, unlimited waits beyond 30 seconds, & cleanup happening
once. cases cover early DOM readiness, queued callbacks after
settlement, shadow-root scanning, partial installation failure, &
disposing one wait while another continues.
- the local-browser `waitForSelector.test.ts` adds a held iframe
response followed by delayed element creation. it checks that both
phases share a finite budget & that unlimited or longer waits succeed.
existing cases retain coverage for css, xpath, iframe hops, visibility,
& open/closed shadow roots; short state & timeout tests use HTTP
fixtures so data-page setup does not consume their budget.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes `waitForSelector()` so frame resolution, locator-world readiness,
and the element wait share one timeout, and `timeout: 0` now means
unlimited instead of a fresh 30-second wait.
**Bug Fixes**
- Frame resolution and locator-world readiness time now counts against
the same deadline as the element wait.
- Expired calls cannot start a new wait; retries after a lost execution
context recalculate the remaining budget.
- `timeout: 0` is unlimited while the default remains 30 seconds, and
the Go, Python, and TypeScript SDK RPC clients now skip their response
deadline when a selector wait passes an explicit `0`.
- Waits dispose observers, timers, and the remote handle on failure or
timeout; on success only the handle is released, and cleanup failure
cannot mask the operation's error.
<sup>Written for commit bcd7eeb703cc4ccfaf76f4c098b4874bb7b62533.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/browserbase/stagehand/pull/3146?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="View guided diff"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<a
href="https://www.cubic.dev/action/auto-fix/pr/browserbase/stagehand/3146?returnTo=https%3A%2F%2Fgithub.com%2Fbrowserbase%2Fstagehand%2Fpull%2F3146&source=description"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"><img
alt="Turn on auto-fix"
src="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->