Repository navigation
Description
Summary
When the browser hangs up its WebSocket connection to the test host while flutter test --platform chrome still has suites running, the tool shuts the browser down and reports every remaining test as did not complete without printing anything that explains why. Even with -v, the only trace is Shutting down Chromium.
Expected results
An error naming the cause, the way the two existing browser-exit messages do.
Actual results
01:10 +755: window 1280×1440
[+71385 ms] Shutting down Chromium.
[ +4 ms] Test suite test/all_pages_smoke_test.dart finished.
01:10 +755: window 1280×1440 - did not complete [E]
01:10 +755: window 1440×320 - did not complete [E]
... 39 more
No error, no exit code, nothing on stderr. The browser was launched with --enable-logging=stderr; it emitted nothing at the moment of the disconnect, and the browser process itself did not exit unexpectedly (the tool closed it afterwards).
Root cause
BrowserManager ends the run silently when the channel closes, in packages/flutter_tools/lib/src/test/flutter_web_platform.dart:
_channel.stream.listen(_onMessage, onDone: close);Two diagnostics already exist for a browser that goes away, but neither can fire on this path, because close() marks everything closed before the browser process exits:
BrowserManager's_browser.onExithandler is guarded byif (!_closed), andclose()sets_closed = truefirst.Chromium's process-exit handler is guarded byif (!_didClose && code != 0), andChromium.close()sets_didClose = true.
So the one case where the browser stops serving the test host without its process dying is the only case with no message at all.
Impact
A run that loses its browser is indistinguishable from a run whose tests genuinely failed. On a large suite this prints hundreds of did not complete lines with no cause, and the natural next step (re-running, bisecting the suite, suspecting the tests) is wasted effort. It is intermittent, which makes the missing diagnostic the expensive part.
Version
Flutter 3.47.1 - channel stable (also present on master at 53c174684f2)
macOS 26.0 darwin-arm64, Chrome 151
Activity
gabrimatic commentedon Aug 27, 2026
A fix is ready on the branch
web-test-browser-disconnect.It reports the disconnect before
BrowserManagertears itself down, and adds a regression test intest/commands.shard/hermetic/flutter_web_platform_test.dart.Verified end to end against a real run: killing the test browser's renderer mid-suite now prints
where before it printed nothing at all and the run ended with only
did not completelines.I cannot open the pull request yet: contributors without write access are limited to two open pull requests, and I currently have two. I will open it as soon as one of them lands.
saurabh-mirajkar commentedon Aug 28, 2026
@gabrimatic Thanks for the detailed investigation. I haven't been able to reproduce the original failure yet.
Could you provide the exact steps or command you use to cause the browser's WebSocket connection to the test host to drop while the tests are still running? In particular, what action/event triggers the disconnect in your repro?
Also, provide a complete output of
flutter doctor -vThat will help us reproduce the issue locally.
gabrimatic commentedon Aug 28, 2026
Thanks for looking. Here is a deterministic repro from a clean project.
What triggers the disconnect. In the original occurrence nothing external did it: on a large suite (792 web tests in one tab) the page's renderer died on its own, intermittently, roughly half of runs. The renderer process dying is what closes the WebSocket while the browser process itself stays alive, and that is the path with no diagnostic. To make it deterministic you can kill that renderer yourself.
Repro
Actual output
Nothing explains it, and
-vadds onlyShutting down Chromium.The exit code is 79 and the run ends with a misleadingNo tests were found.Why nothing is printed.
BrowserManagerends the run on channel close:Both existing browser-exit messages are suppressed on this path, because
close()sets_closed = trueandChromium.close()sets_didClose = truebefore the browser process exits, and those flags are exactly what the two messages check. So the one case where the browser stops serving the test host without its process dying is the only case with no message.The branch linked above adds that message; with it applied the same repro prints the disconnect instead of nothing.
flutter doctor -vAlso reproduced on
masterat 53c1746; the code path is unchanged there.saurabh-mirajkar commentedon Aug 28, 2026
Thanks for providing the repro details. I was able to reproduce the issue locally on master. Routing this to the team for further triage.
gabrimatic commentedon Sep 2, 2026
A slot freed up, so the fix is now open as #192198. The branch is rebased onto current master and carries the same change plus the regression test.
gabrimatic commentedon Sep 2, 2026
One more finding while verifying the fix: the process-exit path is silent too. With
flutter test --platform chromerunning the repro above,kill -9the headless browser process itself (the parent, not its renderer) and the run ends with the samedid not completelines and nothing else, on 3.47.1 stable and on master. The two existing exit-code messages cannot fire because the connection closes before the exit code is delivered, andclose()marks everything closed first.#192198 covers both paths: it reports the disconnect at once and lets the exit code arrive before closing, so a dead process is reported with its exit code and a dead page or renderer is reported as such.
[flutter_tools] Report when the browser disconnects during a web test…
[flutter_tools] Report when the browser disconnects during a web test…