fix(client): report a failed BrowserContext.close() instead of resolving - #42070
fix(client): report a failed BrowserContext.close() instead of resolving#42070skvortsov-dev wants to merge 1 commit into
Conversation
A close() that rejected left _closingStatus === 'closing' behind, so isClosed() reported true for a context that was still live, and every later close() returned early through the entry guard without doing anything. Callers could neither detect the leftover context nor close it: the context stayed alive until the browser exited. Remember the failure, restore the status so isClosed() stays truthful, and rethrow it on subsequent calls.
|
@microsoft-github-policy-service agree |
ae39d32 to
b270a9e
Compare
|
Ran the checks from CONTRIBUTING.md locally on macOS (Chromium):
The new test also fails as expected without the client change, on One note on portability: the test makes |
|
Not rejecting the safety try/catch outright, but the issue is with the har path not being validated for writing. |
|
@pavelfeldman Fair point on the HAR path — validating it upfront makes sense regardless. The HAR was just how I triggered it, though: the shortest way to make I also owe you an honest update on the "we hit this in our suite" part. While building tripwires to catch which await was failing on our CI, I found that our own leaks had a different cause entirely — our harness registered its cleanup But the tripwire work is exactly why I still think this one is worth fixing: to tell "close failed" from "close never ran" from "close succeeded", I had to instrument everything, because a rejected So: validating the HAR path removes one trigger; keeping |
|
Closing as agentic slop - it caches close error for no good reason and masks underlying issues. |
Fixes #42069.
The problem
BrowserContext.close()sets_closingStatus = 'closing'before its firstawaitand never restores it if one of the four awaited steps throws. SinceisClosed()reports'closing'as closed, the entry guard becomes permanent: after one failed close the context can never be closed again, and every laterclose()— from anywhere in the process — resolves as success while doing nothing.The context stays fully usable meanwhile (navigates, screenshots, opens new pages) and keeps being listed in
browser.contexts(), so nothing in the public API tells the caller it is still there.Reproduction (public API only, no monkey-patching):
The change
Remember the failure, restore the status so
isClosed()stays truthful, and rethrow it on subsequent calls, soclose()never claims success for a context it did not close.close()isClosed()after the failuretruefalseclose()callsclose()twicebrowser.close()afterwardsThe added test in
browsercontext-basic.spec.tscovers the first three rows; the existingclose() should be callable twicecovers the fourth.Why the failure is cached rather than retried
I first tried the smaller change of only restoring
_closingStatus, letting every call retry the close for real. The first two attempts then rejected correctly, but the third never settled (still pending after 60 s), so the server-side close path does not appear to survive repeated attempts once a HAR export has failed. Caching the failure keeps the client honest without depending on that.If you would rather have real retries — which is the nicer semantic — the server side likely needs a fix underneath, and I am happy to look into that instead; just let me know which direction you prefer.