[LiveView] LiveReload socket failing to connect on `live` routes

I’ve installed LiveView into an existing Phoenix project (originally created on Phoenix 1.4.16) following this guide. I’m currently working on a LiveView that’s rendered directly in my router: i.e. live "/url", LiveViewModule. I’ve added a regex for this source file to my live_reload config in dev.exs, and I see the [debug] Live reload logline when I make changes to this file.

However, the LiveReload socket for this page gets disconnected immediately after the page loads. In Chrome, the socket to ws://localhost:4000/phoenix/live_reload/socket/websocket?vsn=2.0.0 errors with “WebSocket is closed before the connection is established.”

This only happens on this live route; other views (rendered through get routes and normal controllers and views) live-reload fine.

How might I go about debugging the LiveReload socket and why it’s getting disconnected on live routes?

Were you able to sort this out?

If not you can try to generate a new Phoenix application with --live (the guide you linked has the command) and compare the generated files with the ones in your project. That’d be app/js, Endpoint, dev config, Plug.Session config and a few main others.

1 Like

Ran into this issue today. By any chance, is the app.js <script> tag that connects your LiveView socket outside the <head> tag? Mine was, and live_reload wasn’t working on my live routes. Stuck the app script tag back inside <head> and live_reload is working as expected.

EDIT: Nope. I am mistaken. live_reload was working because the LiveView socket wasn’t correctly connecting. Definitely seeing this issue in an existing app, but not in a newly generated app (and have gone through all the relevant files I can think of).

Thanks @bob, that got me poking around and I noticed something weird:

The data-phx-main element (which as far as I can tell is injected by LiveView itself?) is getting inserted before the <!DOCTYPE html> and everything else in my layout, which causes Safari and Chrome at least to mis-parse the rest of the doc such that the live_reload iframe doesn’t get added to the DOM (i.e. document.getElementsByTagName('iframe') returns a blank list).

Oh, man. Nice catch @feifan. I had noticed that <div>, but I didn’t pay attention to its placement.

I’m also getting some weird markup. My <head> tag is now empty, and that inserted <div data-phx-main ...> is the first tag inside the <body>, with the rest of the <head> elements moved inside the <body> tag.

I notice that is not occurring in a freshly generated liveview app, so now I need to hunt down what is screwing up the HTML source.

EDIT: Okay, looks like I got it working. I had to add plug :put_root_layout into the pipeline, and it stopped screwing up the markup and connected both my LiveView and the live_reload socket.

1 Like

Update: Correct markup also results from specifying a layout on the live route itself, without using plug :put_root_layout. Bad markup seems to only occur when there is no root or route-specific layout specified in the router, and the layout is specified on the LiveView module itself.

1 Like

Sorry for the delay here, just getting back to this part of my project :slight_smile:
Yes that’s absolutely right! I noticed the same thing in my project and setting a root layout fixed it. Thanks for digging into that.


I’m also getting some weird markup. My <head> tag is now empty, and that inserted <div data-phx-main ...> is the first tag inside the <body> , with the rest of the <head> elements moved inside the <body> tag.

That’s because you’re probably looking at the Elements browser inspect, which shows the parsed DOM tree that the browser comes up with. If you actually right-click-view-source (or cURL your endpoint) you’d have seen the HTML I had in my screenshot, which the browser did its best to parse (apparently by assuming that the entire response is body, then adding an empty <head> to make it a valid page in-browser).