Repository navigation
Bad state: StreamSink is bound to a stream #5554
Copy link
Copy link
Closed
flutter/flutter
#171081Labels
in debuggingRelates to the debug adapter or process of running debug sessionsRelates to the debug adapter or process of running debug sessionsin flutterRelates to running Flutter appsRelates to running Flutter appsis bugrelies on sdk changesSomething that requires changes in the Dart/Flutter SDK to ship before it will become availableSomething that requires changes in the Dart/Flutter SDK to ship before it will become available
Milestone
Description
While testing changes for #5552 I frequently see the following error when I trigger Save All:
This message appears to be coming from the DA:
[2:58:02 PM] [DAP] [Info] ==> {"command":"hotReload","arguments":{"debounce":true,"onlyFlutter":true,"reason":"save"},"type":"request","seq":34}
[2:58:02 PM] [DAP] [Info] ==> {"command":"hotReload","arguments":{"debounce":true,"onlyFlutter":true,"reason":"save"},"type":"request","seq":35}
[2:58:02 PM] [DAP] [Info] ==> {"command":"hotReload","arguments":{"debounce":true,"onlyFlutter":true,"reason":"save"},"type":"request","seq":36}
[2:58:02 PM] [DAP] [Info] ==> {"command":"hotReload","arguments":{"debounce":true,"onlyFlutter":true,"reason":"save"},"type":"request","seq":37}
[2:58:02 PM] [DAP] [Info] <== {"seq":186,"type":"event","body":{"message":"Hot reloading…","progressId":"hotReload_26","title":"Flutter"},"event":"dart.progressStart"}
[2:58:02 PM] [DAP] [Info] ==> {"command":"hotReload","arguments":{"debounce":true,"onlyFlutter":true,"reason":"save"},"type":"request","seq":38}
[2:58:02 PM] [DAP] [Info] <== {"seq":187,"type":"event","body":{"message":"==> [Flutter] [{\"id\":25,\"method\":\"app.restart\",\"params\":{\"appId\":\"8551bca2-7d6f-42f9-8c8f-a8528f2e607f\",\"fullRestart\":false,\"pause\":true,\"reason\":\"save\",\"debounce\":true}}]\n"},"event":"dart.log"}
[2:58:02 PM] [DAP] [Info] <== {"seq":188,"type":"event","body":{"message":"Hot reloading…","progressId":"hotReload_27","title":"Flutter"},"event":"dart.progressStart"}
[2:58:02 PM] [DAP] [Info] <== {"seq":189,"type":"event","body":{"message":"==> [Flutter] [{\"id\":26,\"method\":\"app.restart\",\"params\":{\"appId\":\"8551bca2-7d6f-42f9-8c8f-a8528f2e607f\",\"fullRestart\":false,\"pause\":true,\"reason\":\"save\",\"debounce\":true}}]\n"},"event":"dart.log"}
[2:58:02 PM] [DAP] [Info] <== {"seq":190,"type":"event","body":{"progressId":"hotReload_27"},"event":"dart.progressEnd"}
[2:58:02 PM] [DAP] [Info] <== {"seq":191,"type":"response","body":{"error":{"format":"{message}","id":1,"variables":{"message":"Bad state: StreamSink is bound to a stream","stack":"#0 _StreamSinkImpl._controller (dart:io/io_sink.dart:235:7)\n#1 _StreamSinkImpl.add (dart:io/io_sink.dart:155:5)\n#2 _IOSinkImpl.write (dart:io/io_sink.dart:293:5)\n#3 _Socket.write (dart:io-patch/socket_patch.dart:2766:36)\n#4 _StdSink.writeln (dart:io/stdio.dart:442:13)\n#5 ProcessUtils._writeToStdinUnsafe.writeFlushAndComplete (package:flutter_tools/src/base/process.dart:288:15)\n#6 _rootRun (dart:async/zone.dart:1525:13)\n#7 _CustomZone.run (dart:async/zone.dart:1422:19)\n#8 _runZoned (dart:async/zone.dart:2034:6)\n#9 runZonedGuarded (dart:async/zone.dart:2020:12)\n#10 ProcessUtils._writeToStdinUnsafe (package:flutter_tools/src/base/process.dart:297:5)\n#11 ProcessUtils.writelnToStdinUnsafe (package:flutter_tools/src/base/process.dart:255:11)\n#12 FlutterDebugAdapter.sendFlutterMessage (package:flutter_tools/src/debug_adapters/flutter_adapter.dart:339:24)\n#13 FlutterDebugAdapter.sendFlutterRequest (package:flutter_tools/src/debug_adapters/flutter_adapter.dart:321:11)\n#14 FlutterDebugAdapter._performRestart (package:flutter_tools/src/debug_adapters/flutter_adapter.dart:684:13)\n#15 FlutterDebugAdapter.customRequest (package:flutter_tools/src/debug_adapters/flutter_adapter.dart:191:15)\n#16 BaseDebugAdapter.handle (package:dds/src/dap/base_debug_adapter.dart:141:20)\n#17 BaseDebugAdapter.handleIncomingRequest (package:dds/src/dap/base_debug_adapter.dart:477:7)\n#18 BaseDebugAdapter._handleIncomingMessage (package:dds/src/dap/base_debug_adapter.dart:311:7)\n#19 ByteStreamServerChannel._readMessage (package:dds/src/dap/protocol_stream.dart:82:18)\n#20 ByteStreamServerChannel.listen.<anonymous closure> (package:dds/src/dap/protocol_stream.dart:53:24)\n#21 _rootRunUnary (dart:async/zone.dart:1538:47)\n#22 _CustomZone.runUnary (dart:async/zone.dart:1429:19)\n#23 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1329:7)\n#24 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:381:11)\n#25 _DelayedData.perform (dart:async/stream_impl.dart:573:14)\n#26 _PendingEvents.handleNext (dart:async/stream_impl.dart:678:11)\n#27 _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:649:7)\n#28 _rootRun (dart:async/zone.dart:1517:47)\n#29 _CustomZone.run (dart:async/zone.dart:1422:19)\n#30 _CustomZone.runGuarded (dart:async/zone.dart:1321:7)\n#31 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1362:23)\n#32 _rootRun (dart:async/zone.dart:1525:13)\n#33 _CustomZone.run (dart:async/zone.dart:1422:19)\n#34 _CustomZone.runGuarded (dart:async/zone.dart:1321:7)\n#35 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1362:23)\n#36 _microtaskLoop (dart:async/schedule_microtask.dart:42:23)\n#37 _startMicrotaskLoop (dart:async/schedule_microtask.dart:60:5)\n#38 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:127:13)\n#39 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:194:5)\n"}}},"command":"hotReload","message":"Bad state: StreamSink is bound to a stream","request_seq":35,"success":false}
Bad state: StreamSink is bound to a stream
#0 _StreamSinkImpl._controller (dart:io/io_sink.dart:235:7)
#1 _StreamSinkImpl.add (dart:io/io_sink.dart:155:5)
#2 _IOSinkImpl.write (dart:io/io_sink.dart:293:5)
#3 _Socket.write (dart:io-patch/socket_patch.dart:2766:36)
#4 _StdSink.writeln (dart:io/stdio.dart:442:13)
#5 ProcessUtils._writeToStdinUnsafe.writeFlushAndComplete (package:flutter_tools/src/base/process.dart:288:15)
#6 _rootRun (dart:async/zone.dart:1525:13)
#7 _CustomZone.run (dart:async/zone.dart:1422:19)
#8 _runZoned (dart:async/zone.dart:2034:6)
#9 runZonedGuarded (dart:async/zone.dart:2020:12)
#10 ProcessUtils._writeToStdinUnsafe (package:flutter_tools/src/base/process.dart:297:5)
#11 ProcessUtils.writelnToStdinUnsafe (package:flutter_tools/src/base/process.dart:255:11)
#12 FlutterDebugAdapter.sendFlutterMessage (package:flutter_tools/src/debug_adapters/flutter_adapter.dart:339:24)
#13 FlutterDebugAdapter.sendFlutterRequest (package:flutter_tools/src/debug_adapters/flutter_adapter.dart:321:11)
#14 FlutterDebugAdapter._performRestart (package:flutter_tools/src/debug_adapters/flutter_adapter.dart:684:13)
#15 FlutterDebugAdapter.customRequest (package:flutter_tools/src/debug_adapters/flutter_adapter.dart:191:15)
#16 BaseDebugAdapter.handle (package:dds/src/dap/base_debug_adapter.dart:141:20)
#17 BaseDebugAdapter.handleIncomingRequest (package:dds/src/dap/base_debug_adapter.dart:477:7)
#18 BaseDebugAdapter._handleIncomingMessage (package:dds/src/dap/base_debug_adapter.dart:311:7)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
in debuggingRelates to the debug adapter or process of running debug sessionsRelates to the debug adapter or process of running debug sessionsin flutterRelates to running Flutter appsRelates to running Flutter appsis bugrelies on sdk changesSomething that requires changes in the Dart/Flutter SDK to ship before it will become availableSomething that requires changes in the Dart/Flutter SDK to ship before it will become available

Activity
DanTup commentedon Jun 18, 2025
@bkonyi do you have any ideas about this? The call in DAP here
sendFlutterMessageis writing to thestdinprocess of theflutter run --machineprocess that it spawned. It seems to fail in this way sometimes when multiple Hot Reloads are sent at the same time (whether this is because of the hot reload that's occurring, or the multiple writes in a small space of time, I'm not sure).The write goes through a method named
ProcessUtils.writelnToStdinUnsafe, though it's not clear to me what's unsafe about it. Are we not allowed to call it again until the previous call as completed? If so, is there something better we can do than queueing every write to the process from the DAP? (it seems like anyone usingProcessUtils.writelnToStdinUnsafewould probably rather they queue than throw?).bkonyi commentedon Jun 23, 2025
So
ProcessUtils.writelnToStdinUnsafecallsstdin.flush()here which marks the sink as bound until the flush is complete. The returned future fromwritelnToStdinUnsafecompletes when the flush operation is finished, but if we have an asynchronous gap and try to callwritelnToStdinUnsafeagain, it's possible to try and write to the sink while aflush()is in progress.If we're going to use
ProcessUtils.writelnToStdinUnsafe, we're going to either need to use a mutex aroundstdinto ensure we only have a single writer to the sink at a given time or we'll have to make some sort of queue. I'm not 100% sure why we need this wrapper, so it could be that we can get away with just writing tostdindirectly and not flushing.DanTup commentedon Jun 24, 2025
Oh, I think this is related to flutter/flutter#137184. Presumably without an explicit
flush()then if it fails (eg. because the process terminates), it could throw an unhandled exception. Theflushforces the caller to handle deal with that exception.So I think probably the best thing is for
sendFlutterMessage()to simply queue each write on the end of the previous one. I feel like there are probably more callers writing to processes that might want this behaviour so I don't know if it's the right place to add it, but I guess I can add it here and it can always be extracted later if other places want this.[flutter_tool] Migrate DAP off `ProcessUtils.writelnToStdinUnsafe`
[flutter_tool] Migrate DAP off `ProcessUtils.writelnToStdinUnsafe` (#…
DanTup commentedon Jun 25, 2025
This was fixed by flutter/flutter@7deecab.
[flutter_tool] Migrate DAP off `ProcessUtils.writelnToStdinUnsafe` (f…
[flutter_tool] Migrate DAP off `ProcessUtils.writelnToStdinUnsafe` (#…