Repository navigation
flutter test --platform chrome hangs forever when Chromium cannot bind its remote debugging port #192013
Description
Steps to reproduce
ChromiumLauncher._spawnChromiumProcess waits for Chromium to print DevTools listening on stderr with no timeout, and its orElse fires only when stderr closes. A Chromium that starts, fails to bind its remote debugging port, and keeps running satisfies neither condition, so the await never returns and flutter test --platform chrome hangs forever with no verdict.
The port is chosen by OperatingSystemUtils.findFreePort, whose own doc comment states the race:
Returns an unused network port. […] The port returned by this function may become used before it is bound by its intended user.
To reproduce deterministically, put the launcher into the state that race produces — the chosen port already taken:
- Bind the port on both loopback stacks (this matters, see below):
import socket, time
port = 9555
s4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s4.bind(("127.0.0.1", port)); s4.listen(5)
s6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
s6.bind(("::1", port)); s6.listen(5)
time.sleep(600)-
Point
CHROME_EXECUTABLEat a shim that rewrites the tool's--remote-debugging-port=<n>to that port andexecs the real Chrome. -
flutter test --platform chrome --verbosein any project with at least one test.
Expected results
The run fails with a message naming the problem, or the existing 3-try retry in _spawnChromiumProcess engages.
Actual results
The run hangs indefinitely with no verdict. --verbose stops here and never advances (I left it over two minutes; a separate wild instance of the same class of hang sat unchanged for 14 minutes):
00:00 +0: loading <app>/test/models/some_test.dart
[+7814 ms] Loading test suite test/models/some_test.dart.
[ ] Serving tests at http://localhost:64122/static/index.html?managerUrl=...
[ ] Launching Chromium (url = ..., headless = true, skipCheck = false, debugPort = null)
[ +251 ms] [CHROME]: [ERROR:net/socket/socket_posix.cc:153] bind() failed: Address already in use (48)
[ ] [CHROME]: [ERROR:net/socket/socket_posix.cc:153] bind() failed: Address already in use (48)
[ ] [CHROME]: [ERROR:content/browser/devtools/devtools_http_handler.cc:313] Cannot start http server for devtools.
The dart process stays alive at 0% CPU. Chromium stays alive with stderr open. Nothing times out.
Why the existing retry does not save it
orElse runs only when the stderr stream closes. Chromium here does not exit, so the stream never closes, so the retry >= kMaxRetries / throwToolExit path in orElse is unreachable.
Verified Chrome behaviour (Chrome for Testing 145.0.7632.6, macOS arm64)
- Debug port occupied on IPv4 only → Chrome falls back to IPv6, prints
DevTools listening on ws://[::1]:<port>/..., and the tool proceeds. No hang. (It may then be talking to the wrong server, but that is a separate matter.) - Debug port occupied on both stacks → Chrome prints
Cannot start http server for devtools., stays alive with stderr open, and never prints the marker. This is the hang.
Suggested fix
Bounding the wait feeds the retry logic that is already written. In packages/flutter_tools/lib/src/web/chrome.dart, _spawnChromiumProcess:
final Future<String> devToolsLine = process.stderr
.transform(utf8LineDecoder)
.map(...)
.firstWhere((String line) => line.startsWith('DevTools listening'), orElse: ...);
var timedOut = false;
try {
await devToolsLine.timeout(const Duration(seconds: 30));
} on TimeoutException {
timedOut = true;
shouldRetry = true;
}
if (!hitGlibcBug && !shouldRetry) {
return process;
}
if (timedOut && retry >= kMaxRetries) {
errors.forEach(_logger.printError);
throwToolExit(
'Chromium started but never reported "DevTools listening" within 30 seconds, '
'$kMaxRetries times over. Its debugging port is most likely already in use.',
);
}
retry += 1;I applied exactly this to a local 3.41.9 checkout, rebuilt flutter_tools.snapshot, and re-ran the reproduction: the infinite hang became Failed to launch browser after 3 tries. in 41 seconds. I have since reverted the patch; my checkout is clean.
One caveat on that patch: launch() picks the port once, before the retry loop, so all three retries reuse the same occupied port. Bounding the wait is still a strict improvement (a loud, bounded failure instead of an infinite hang), but re-picking the port per retry would let it actually recover.
flutter doctor -v
[✓] Flutter (Channel stable, 3.41.9, on macOS 26.5.2 25F84 darwin-arm64, locale en-US)
• Flutter version 3.41.9 on channel stable at /opt/homebrew/share/flutter
• Framework revision 00b0c91f06 (2026-04-29), Engine revision 42d3d75a56
• Dart version 3.11.5, DevTools version 2.54.2
[✓] Xcode - develop for iOS and macOS (Xcode 26.6)
[✗] Chrome - develop for the web (no /Applications Chrome; CHROME_EXECUTABLE points at
Chrome for Testing 145.0.7632.6)
[✓] Network resources
Activity
SaiWebApps commentedon Aug 29, 2026
Possibly related, filed at the same time: #192014 — a different, intermittent way for the same command to hang with no verdict, in the engine (a CanvasKit init promise that never resolves) rather than the launcher. Distinct root cause; both are unbounded waits on the path to loading a web test suite.
mbcorona commentedon Aug 29, 2026
Hi @SaiWebApps, thanks for the detailed report. Routing to the web team.
Confirmed: _spawnChromiumProcess awaits devToolsListening.future with no timeout, and it only completes when the marker prints or the process exits.
mbcorona commentedon Sep 1, 2026
Note --platform chrome is deprecated (use -d chrome or integration_test), but this same launcher backs flutter run -d chrome, so the missing timeout is worth fixing regardless.
SaiWebApps commentedon Sep 3, 2026
Draft reply — #192013 (ChromiumLauncher waits without timeout for "DevTools listening")
You asked us to update Flutter and our dependencies and re-test. Done, and
this one still reproduces.
Setup: Flutter 3.47.2 stable (d3b14c8), full
flutter pub upgrade, macOS,Chrome for Testing via Playwright. Method: 80 consecutive runs of our full
flutter test --platform chromesuite (46 files / 327 tests) with our stalldetector recording instead of retrying (a stall = 48 s of no output while the
process tree burns ~0 CPU).
Result: 2 of 80 runs stalled, both with this issue's exact signature —
zero tests completed, the reporter frozen at its first
loadingline, andlsofshowing the launched Chromium itself LISTENing on the debugging portflutter_tools assigned it:
…while
ChromiumLauncher._spawnChromiumProcesssat in its no-timeoutfirstWhere((l) => l.startsWith('DevTools listening'))on stderr. The rate(2/80) is statistically indistinguishable from what we measured on 3.41.9
(1/27).
Happy to attach the two full diagnosis logs (process tree, port holders, last
reporter line) if useful. A bounded wait — or treating "Cannot start http
server for devtools" on stderr as terminal — would turn this from a
never-exits hang into an ordinary retryable failure.
chore(flutter): retest the two filed hangs on Flutter 3.47.2; runner …
[web] Fix hang when Chromium cannot bind remote debugging port (flutt…