Skip to content

Web hot reload breaks app when running as Web Server (-d web-server) #60289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
agreaves opened this issue Mar 9, 2025 · 21 comments · Fixed by flutter/flutter#165820 or flutter/flutter#168400
Assignees
Labels
area-web-js Issues related to JavaScript support for Dart Web, including DDC, dart2js, and JS interop. P1 A high priority bug; for example, a single project is unusable or has many test failures web-dev-compiler web-hot-reload Issues related to stateful hot reload on the web

Comments

@agreaves
Copy link

agreaves commented Mar 9, 2025

Steps to reproduce

  1. Create an empty Flutter project
  2. Run flutter run -d web-server --web-experimental-hot-reload

Notice that the served Flutter application fails to load and just renders a blank screen.

Failure Case

Running flutter run -d web-server --web-experimental-hot-reload and opening relevant localhost (http://localhost:53392):

Image

WAI Cases

Running flutter run -d web-server and opening relevant localhost (http://localhost:53392):

Image

Running flutter run -d chrome --web-experimental-hot-reload:

Image

Code sample

N/A (always breaks regardless of app details)

Flutter version

Flutter version observing the above behavior.

Flutter 3.30.0-1.0.pre.520 • channel master • https://github.com/flutter/flutter.git
Framework • revision a7e276a20d (2 days ago) • 2025-03-07 18:43:08 -0800
Engine • revision a7e276a20d (2 days ago) • 2025-03-07 18:43:08 -0800
Tools • Dart 3.8.0 (build 3.8.0-149.0.dev) • DevTools 2.43.0

Side note: I tried upgrading to the latest version on master and running on chrome (with hot reload) but that failed entirely. I won't file another ticket as I'm guessing it will be fixed soon, but just in case here's the Flutter version for that:

Flutter 3.30.0-1.0.pre.525 • channel master • https://github.com/flutter/flutter.git
Framework • revision 93c8ed0775 (60 minutes ago) • 2025-03-09 12:36:24 -0400
Engine • revision 93c8ed0775 (60 minutes ago) • 2025-03-09 12:36:24 -0400
Tools • Dart 3.8.0 (build 3.8.0-149.0.dev) • DevTools 2.43.0

Second side note: This is extremely impressive work and such a major value add to the Flutter community! Congrats to all involved! 🎉

@agreaves agreaves added area-web-js Issues related to JavaScript support for Dart Web, including DDC, dart2js, and JS interop. web-dev-compiler web-hot-reload Issues related to stateful hot reload on the web labels Mar 9, 2025
@nshahan
Copy link
Contributor

nshahan commented Mar 10, 2025

Thanks for the report!

The docs here describe the web-server flag as a way to open the app in any browser of your choice. https://docs.flutter.dev/platform-integration/web/building#run-your-app

AFAIK the hot reload support is tightly coupled with Chrome DevTools to control the execution and synchronize events in the Dart tooling while libraries are being replaced in the browser at runtime. We don't have any plans to expand that to support developer tools in other browsers at this time.

@srujzs @jyameo
Is that assessment correct? Do you think we should print a warning or error if you try and pass
-d web-server --web-experimental-hot-reload? Would there be any way we could support a random chrome instance that loads the page that wasn't launched by flutter tools?

@biggs0125
Copy link

Can we confirm whether or not hot restart was working when running with the 'web-server' device? If so it seems like adding similar support for hot reload should be doable. But if not, I don't think we need to add it at the moment.

More generally though, even if hot reload isn't supported, this implies our new module system isn't working with this run configuration. I repro-ed this locally and didn't see any errors in the console. I think the main isn't getting invoked. I believe the bootstrap script it's producing is still referencing the loader for the old module system instead of the dartDevEmbedder:
https://github.com/flutter/flutter/blob/b16430b2fd57a5aa6b6c680cdbda5582506fe120/packages/flutter_tools/lib/src/web/bootstrap.dart#L485

@srujzs
Copy link
Contributor

srujzs commented Mar 10, 2025

I do see us using the dartDevEmbedder with the given flags, but the issue is that we don't have any of the code that DWDS injects since we're not using Chrome.

With AMD, this doesn't seem to be an issue, because require.js implicitly handles the needed dependencies, but with the DDC library bundle format, we need DWDS (or something...) to load the necessary sources. Since we never call that load, main is never triggered.

Hot restart also requires a hard refresh of the page with the web server (probably because DWDS isn't there to handle it correctly).

@biggs0125
Copy link

It makes sense that a refresh is required given nothing is attached to the running browser. So in that case hot reload itself doesn't make sense in this context. I don't think there's a need to try to attach DWDS onto the Chrome process in this web-server case.

But we do need apps to run in this environment. We will need to figure out what should be loading the sources into the page for the dartDevEmbedder. More generally, we should decouple the initial sources loading from DWDS. Hot reload and hot restart will require a running debug service but the program should still run in environments where those features aren't supported.

Can we inject information into the bootstrap script that allows it to load the sources itself?

@srujzs
Copy link
Contributor

srujzs commented Mar 10, 2025

Can we inject information into the bootstrap script that allows it to load the sources itself?

We could reuse some of the DWDS code to do that part: https://github.com/dart-lang/webdev/blob/8f146a15fba4d7c0dc76b51e083a5459216126cf/dwds/lib/src/handlers/injector.dart#L95 without needing the injected client or any of the code to set up DWDS debugging. Separating out that latter part so we don't try and include it and just setting up the right code to handle loading and fetching sources will require work in DWDS.

It's likely preferable to use DWDS for that purpose instead of reimplementing all the logic to parse module metadata and set up paths correctly.

FYI @bkonyi

@jyameo jyameo self-assigned this Mar 12, 2025
@jyameo
Copy link
Contributor

jyameo commented Mar 12, 2025

I can take a look at this issue. I'll follow Srujan's suggestion to reuse parts of DWDS in Flutter tools to load the necessary files. will reach out if I have any questions or run into issues.

@a-siva a-siva added the P1 A high priority bug; for example, a single project is unusable or has many test failures label Mar 13, 2025
@kkoskenvirta
Copy link

Can we confirm whether or not hot restart was working when running with the 'web-server' device? If so it seems like adding similar support for hot reload should be doable. But if not, I don't think we need to add it at the moment.

More generally though, even if hot reload isn't supported, this implies our new module system isn't working with this run configuration. I repro-ed this locally and didn't see any errors in the console. I think the main isn't getting invoked. I believe the bootstrap script it's producing is still referencing the loader for the old module system instead of the dartDevEmbedder: https://github.com/flutter/flutter/blob/b16430b2fd57a5aa6b6c680cdbda5582506fe120/packages/flutter_tools/lib/src/web/bootstrap.dart#L485

I don't know how it works under the hood but i am running the web-server device commonly and using hot-reload on every time i save a file. Atleast that triggers the hot-reload, when running in debug I am also required to attach the debugger with the browser add-on.

@jyameo
Copy link
Contributor

jyameo commented Mar 20, 2025

Can we confirm whether or not hot restart was working when running with the 'web-server' device? If so it seems like adding similar support for hot reload should be doable. But if not, I don't think we need to add it at the moment.
More generally though, even if hot reload isn't supported, this implies our new module system isn't working with this run configuration. I repro-ed this locally and didn't see any errors in the console. I think the main isn't getting invoked. I believe the bootstrap script it's producing is still referencing the loader for the old module system instead of the dartDevEmbedder: https://github.com/flutter/flutter/blob/b16430b2fd57a5aa6b6c680cdbda5582506fe120/packages/flutter_tools/lib/src/web/bootstrap.dart#L485

I don't know how it works under the hood but i am running the web-server device commonly and using hot-reload on every time i save a file. Atleast that triggers the hot-reload, when running in debug I am also required to attach the debugger with the browser add-on.

That's interesting... a couple of us were able to reproduce the issue consistently, so I'm surprised to hear that hot reload is working for you in this context. Could you share more details about the Flutter/Dart version you're using?

I'm currently rolling out a fix that will reuse parts of DWDS to handle source loading without requiring the injected client or DWDS debugging.

github-merge-queue bot pushed a commit to flutter/flutter that referenced this issue Mar 24, 2025
…165820)

This change ensures that `injectDebuggingSupportCode` in DWDS is set
based on the specified device ID, defaulting to Chrome. This complements
the changes made in dart-lang/webdev#2601 and
ensures that debugging support is correctly configured for different
environments.

fixes dart-lang/sdk#60289
@github-project-automation github-project-automation bot moved this from In Progress to Done in Web Hot Reload - Known Issues Mar 24, 2025
@agreaves
Copy link
Author

Hey folks! Thanks so much for looking into this. Unfortunately this issue is still reproducible (via the original steps) even in the master or beta channels. Could we re-open the issue? It's quite an important use case for us.

@jyameo
Copy link
Contributor

jyameo commented May 2, 2025

Hey folks! Thanks so much for looking into this. Unfortunately this issue is still reproducible (via the original steps) even in the master or beta channels. Could we re-open the issue? It's quite an important use case for us.

@agreaves Thanks for the follow-up! I’ll take a look and see what’s going on. Appreciate your patience.

@jyameo
Copy link
Contributor

jyameo commented May 2, 2025

I took a look at the issue, and I noticed something interesting. When I run the app directly from VS Code using -d web-server --web-experimental-hot-reload, the page loads as expected. But when I run the same command from the terminal and manually open the app URL in the browser, the page appears blank.

The only difference I can see is that VS Code launches the browser automatically, whereas from the terminal, I have to open it manually. My hunch is that this might be related to how or when the bootstrap scripts are loaded; since the main entrypoint runs as soon as the scripts are loaded, maybe there's a timing issue or race condition at play.

@srujzs Do you have any thoughts on what might be causing this discrepancy between the two methods?

For reference, here's my launch.json configuration in VS Code:

{
  "name": "Web-server with Hot-reload ENABLED",
  "type": "dart",
  "request": "launch",
  "program": "lib/main.dart",
  "args": [
    "-d",
    "web-server",
    "--web-experimental-hot-reload"
  ]
}

@srujzs
Copy link
Contributor

srujzs commented May 2, 2025

The difference between VS code and running it directly might be due to this: https://github.com/flutter/flutter/blob/07b09836576b48278774cda9084da1d451fc258f/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart#L160

It looks like VS code starts Flutter with --start-paused for some reason. When using the web server, DWDS is only enabled when this flag is set: https://github.com/flutter/flutter/blob/07b09836576b48278774cda9084da1d451fc258f/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart#L166

Without enabling DWDS, the scripts are never added to the bootstrap, and nothing runs.

flutter run -d web-server --web-experimental-hot-reload --start-paused makes this work. I'm not sure why --start-paused is needed though.

@bkonyi
Copy link
Contributor

bkonyi commented May 2, 2025

I'm not sure why --start-paused is needed though.

I'm 99% sure this is so VSCode can set breakpoints set before the application has run (@DanTup, can you confirm?).

@DanTup
Copy link
Collaborator

DanTup commented May 2, 2025

I'm 99% sure this is so VSCode can set breakpoints set before the application has run (@DanTup, can you confirm?).

This is correct. If we don't start the app paused and you have breakpoints in code that runs very early, then it may have already executed before we were able to add the breakpoint. So we must start the app paused, send breakpoints, and then resume.

Btw, when running from VS Code, it will also provide some other flags like --machine that may affect the code path taken in Flutter tools. Generally the web-server device is used in contexts where Chrome cannot be spawned (for example in a remote workspace or browser-based IDE with a remote container) and the Dart Debug extension is used as a proxy for the debugger (whereas when launching Chrome, flags are used to open a debug port instead).

@jyameo
Copy link
Contributor

jyameo commented May 2, 2025

Thanks all! That makes a lot of sense. It's interesting because flutter run -d web-server seems to work without the --start-paused flag, while flutter run -d web-server --web-experimental-hot-reload appears to require it for the app to load properly. I’ll investigate further to understand exactly what’s going on in both cases, particularly whether --start-paused is strictly required to run on a web-server with the new DDC Library Bundle format. Will report back once I have more clarity.

github-merge-queue bot pushed a commit to flutter/flutter that referenced this issue May 13, 2025
… tests (#168400)

This PR makes the following improvements to web hot reload support and
related tests:

- The `--start-paused` flag is now silently added when running with `-d
web-server --web-experimental-hot-reload` if it is not already present.
- Refactored test infrastructure to allow specifying the device
parameter (e.g., `'chrome'`, `'web-server'`) when running integration
tests.
- Added a new `web_run_web_server_test` to run flows on the web-server
device.
- Updated existing tests to explicitly pass the correct device parameter
where needed.
- We are currently not able to test hot reload on the web-server device
as this is not yet supported.

Fixes dart-lang/sdk#60289
@jyameo
Copy link
Contributor

jyameo commented May 13, 2025

I added support for silently appending --start-paused when running with -d web-server --web-experimental-hot-reload, ensuring that the app loads properly without requiring manual input. This fix is temporary; will work on implementing a more robust fix in the near future.

@RobertBrunhage
Copy link

So I just tried running this in the terminal and opening chrome to see how this works. Not sure I understand, when opening this url, it's a white screen with these logs and no way to hit "r" in the terminal to trigger a hot-reload.

Injecting <script> tag. Using callback.
ddc_module_loader.js:1014 DDC is about to load 1/2 scripts with pool size = 1000
ddc_module_loader.js:1014 DDC is about to load 306/306 scripts with pool size = 1000
Launching lib/main.dart on Web Server in debug mode...
Waiting for connection from debug service on Web Server...         11.1s
Waiting for connection from Dart debug extension at http://localhost:57503
The web-server device requires the Dart Debug Chrome extension for debugging. Consider using the Chrome or
Edge devices for an improved development workflow.

I would expect this to behave the same way as without the --web-experimental-hot-reload flag but also enable the hot-reload functionality.

@jyameo
Copy link
Contributor

jyameo commented May 20, 2025

Hi @RobertBrunhage. Thanks for trying it out and for providing some feedback!

Currently, hot reload functionality is not yet supported when running Flutter on -d web-server devices. You should still be able to run the app and access it in any browser, but hot reload isn't available at this time for non Chrome browsers.

If you're seeing a blank screen, my guess is that you're using a Flutter version that doesn’t yet include the changes needed for the app to start automatically. As a temporary workaround, you can manually add the --start-paused flag when launching the app, which should allow it to load properly.

We’re actively working on adding support for hot reload on non-Chrome browsers. Hope this helps clarify things!

@RobertBrunhage
Copy link

@jyameo Thanks for the insight, is there currently any way to run -d chrome in headless mode or similar, and connect with chrome browser to get hot reload?

I am asking this as currently we have an interactive flutter course which we currently allow the user to hot restart. But we would love to have a way to support hot reload as well!

@biggs0125
Copy link

@RobertBrunhage That interactive course seems awesome!

Unfortunately, unless the Chrome instance your app is running on has a debug port open that you can connect to (which by default running Chrome applications don't have) there won't be a way to hot reload. When you use -d chrome we're specifically opening a Chrome process with that debug port open. Even if that were a headless browser, there isn't a way to then connect that headless browser to another Chrome process.

As Jessy alluded to, we're actively working to remove this dependency on Chrome debug ports to do hot reload: dart-lang/webdev#2605

@RobertBrunhage
Copy link

@RobertBrunhage That interactive course seems awesome!

Unfortunately, unless the Chrome instance your app is running on has a debug port open that you can connect to (which by default running Chrome applications don't have) there won't be a way to hot reload. When you use -d chrome we're specifically opening a Chrome process with that debug port open. Even if that were a headless browser, there isn't a way to then connect that headless browser to another Chrome process.

As Jessy alluded to, we're actively working to remove this dependency on Chrome debug ports to do hot reload: dart-lang/webdev#2605

Got it and thanks for the insight!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web-js Issues related to JavaScript support for Dart Web, including DDC, dart2js, and JS interop. P1 A high priority bug; for example, a single project is unusable or has many test failures web-dev-compiler web-hot-reload Issues related to stateful hot reload on the web
Projects
Status: Done