fix(android): reject pending callbacks on device close#40722
Merged
pavelfeldman merged 2 commits intomicrosoft:mainfrom May 8, 2026
Merged
fix(android): reject pending callbacks on device close#40722pavelfeldman merged 2 commits intomicrosoft:mainfrom
pavelfeldman merged 2 commits intomicrosoft:mainfrom
Conversation
AndroidDevice._close() did not reject pending _callbacks entries. Any in-flight _send() call would hang forever if the device was closed before the response arrived. Add callback rejection loop and clear the map, matching the pattern used by CRSession.dispose(), FFSession.dispose(), WKSession.dispose(), and BidiSession.dispose().
Address race condition: _send() has an await (this._driver()) between the implicit closed check and _callbacks.set(). A concurrent _close() can clear the map during that yield, leaving the new callback unresolvable. Add a synchronous _isClosed check after the await.
pavelfeldman
approved these changes
May 8, 2026
Contributor
Test results for "MCP"5 failed 7026 passed, 1068 skipped Merge workflow run. |
Contributor
Test results for "tests 1"4 flaky41692 passed, 850 skipped Merge workflow run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_callbackswhenAndroidDevice._close()is called, preventing in-flight_send()calls from hanging forever_isClosedguard after theawaitin_send()to prevent a race condition where_close()clears the callback map during the microtask yieldProblem
AndroidDevice._close()tears down the driver, backend, and browser connections, but never rejects the pending entries in_callbacks. Any in-flight_send()call that is awaiting a response when the device closes will hang indefinitely because its promise is never resolved or rejected.All four browser protocol sessions (CR, FF, WK, Bidi) already handle this correctly:
The Android device uses the same callback map pattern (
_callbacks: Map<number, { fulfill, reject }>) but was missing the cleanup loop.Additionally,
_send()has a race condition: itawaitsthis._driver()before checking_isClosedand adding to the callback map. A concurrent_close()can clear the map during that yield, leaving the new callback orphaned.Related issues
context.newPage()/launchBrowser()webview.page()hangs forever on hidden/detached WebViewslaunchBrowser()stuck on Oppo/Xiaomi devicesThese issues all involve Android operations hanging or becoming unresponsive. While this fix does not address the root cause of each one (often device-specific firmware issues), it ensures that when the device does close, all pending operations fail cleanly instead of hanging the test process.
Comparable fix in Appium: appium/appium#6562 (hanging after clicking button that closes Android WebView, caused by pending callbacks not cleaned up).
Fix
_close(): iterate_callbacks, reject each withError('Device closed'), then clear the map. Placed before driver/backend teardown so callbacks are rejected while the transport is still valid._send(): addthis._isClosedcheck afterawait this._driver()to close the race window.Changes
packages/playwright-core/src/server/android/android.ts: 4 lines added