Mount being called twice after LiveView dead render

I’m seeing behaviour where mount is being called twice in one of my liveviews after connection.

The mount/3 function looks like this:

  def mount(params, _session, socket) do
    background =
      if params["id"] do
        c = Thing.C.get_c!(params["id"])
        c.data
      else
        nil
      end

    dbg("mount")
    if connected?(socket) do
      Task.async(fn ->
        build_m(background)
      end)
    end

    {:ok,
     assign(socket,
       message: nil,
       m: Arrays.new()
     )}
  end

And this is a request log (note the dbg statements, ‘build’ is called inside the async function):

[info] GET /app_things/16/practice
[debug] Processing with Thing.app_thingLive.app_thing.Elixir.Thing.app_thingLive.app_thing/2
  Parameters: %{"id" => "16"}
  Pipelines: [:browser]
[debug] QUERY OK source="sessions" db=0.4ms idle=1568.8ms
SELECT s0."id", s0."client_id", s0."inserted_at", s0."updated_at" FROM "sessions" AS s0 WHERE (s0."client_id" = $1) ["ik13HWTh9w0E4BusQ95r"]
↳ Thing.InitSessionId.on_mount/4, at: lib/thing/live/init_session_id.ex:10
[debug] QUERY OK source="app_things" db=0.4ms idle=1569.2ms
SELECT c0."id", c0."name", c0."session_id", c0."data", c0."inserted_at", c0."updated_at" FROM "app_things" AS c0 WHERE (c0."id" = $1) [16]
↳ Thing.app_thingLive.app_thing.mount/3, at: lib/thing/live/app_thing_live/app_thing.ex:9
[(sg 0.1.0) lib/thing/live/app_thing_live/app_thing.ex:15: Thing.app_thingLive.app_thing.mount/3]
"mount" #=> "mount"

[info] Sent 200 in 35ms
[info] CONNECTED TO Phoenix.LiveView.Socket in 17µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "WhMIOSIRDhQkD3cNPVVjJnEoG3kgGwY55TOqqNwDOb9EYl5JBoz4NcYr", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4000/assets/app.css", "1" => "http://localhost:4000/assets/app.js", "2" => "http://localhost:4000/assets/app.css", "3" => "http://localhost:4000/assets/app.js"}, "vsn" => "2.0.0"}
[info] CONNECTED TO Phoenix.LiveView.Socket in 16µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "WSIeIxoGTQEiP355NlslCXJ3EgwiFREk6eYkIY4QIR01RbseA0sALmNo", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4000/assets/app.css", "1" => "http://localhost:4000/assets/app.js", "2" => "http://localhost:4000/assets/app.css", "3" => "http://localhost:4000/assets/app.js"}, "vsn" => "2.0.0"}
[debug] MOUNT Thing.app_thingLive.app_thing
  Parameters: %{"id" => "16"}
  Session: %{"_csrf_token" => "oGGHS_yPkmNHd9Vl3GaMnx_K", "client_id" => "ik13HWTh9w0E4BusQ95r", "test" => "test value"}
[debug] QUERY OK source="sessions" db=1.7ms queue=0.3ms idle=1908.3ms
SELECT s0."id", s0."client_id", s0."inserted_at", s0."updated_at" FROM "sessions" AS s0 WHERE (s0."client_id" = $1) ["ik13HWTh9w0E4BusQ95r"]
↳ Thing.InitSessionId.on_mount/4, at: lib/thing/live/init_session_id.ex:10
[debug] QUERY OK source="app_things" db=0.6ms idle=1910.5ms
SELECT c0."id", c0."name", c0."session_id", c0."data", c0."inserted_at", c0."updated_at" FROM "app_things" AS c0 WHERE (c0."id" = $1) [16]
↳ Thing.app_thingLive.app_thing.mount/3, at: lib/thing/live/app_thing_live/app_thing.ex:9
[(sg 0.1.0) lib/thing/live/app_thing_live/app_thing.ex:15: Thing.app_thingLive.app_thing.mount/3]
"mount" #=> "mount"

[(sg 0.1.0) lib/thing/live/app_thing_live/app_thing.ex:153: Thing.app_thingLive.app_thing.build_m/1]
"build" #=> "build"

[debug] Replied in 3ms
[info] CONNECTED TO Phoenix.LiveView.Socket in 15µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "WSIeIxoGTQEiP355NlslCXJ3EgwiFREk6eYkIY4QIR01RbseA0sALmNo", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4000/assets/app.css", "1" => "http://localhost:4000/assets/app.js", "2" => "http://localhost:4000/assets/app.css", "3" => "http://localhost:4000/assets/app.js"}, "vsn" => "2.0.0"}
[debug] MOUNT Thing.app_thingLive.app_thing
  Parameters: %{"id" => "16"}
  Session: %{"_csrf_token" => "oGGHS_yPkmNHd9Vl3GaMnx_K", "client_id" => "ik13HWTh9w0E4BusQ95r", "test" => "test value"}
[debug] QUERY OK source="sessions" db=0.3ms idle=1945.3ms
SELECT s0."id", s0."client_id", s0."inserted_at", s0."updated_at" FROM "sessions" AS s0 WHERE (s0."client_id" = $1) ["ik13HWTh9w0E4BusQ95r"]
↳ Thing.InitSessionId.on_mount/4, at: lib/thing/live/init_session_id.ex:10
[(sg 0.1.0) lib/thing/live/app_thing_live/app_thing.ex:15: Thing.app_thingLive.app_thing.mount/3]
"mount" #=> "mount"

[(sg 0.1.0) lib/thing/live/app_thing_live/app_thing.ex:153: Thing.app_thingLive.app_thing.build_m/1]
"build" #=> "build"

[debug] QUERY OK source="app_things" db=0.2ms idle=1945.7ms
SELECT c0."id", c0."name", c0."session_id", c0."data", c0."inserted_at", c0."updated_at" FROM "app_things" AS c0 WHERE (c0."id" = $1) [16]
↳ Thing.app_thingLive.app_thing.mount/3, at: lib/thing/live/app_thing_live/app_thing.ex:9
[debug] Replied in 1ms

Can anybody suggest where I should be looking to debug this? I’m at a bit of a loss as it seems to happen but it doesn’t seem to be crashing anywhere I can see. It looks like it’s creating two sockets, is that normal? Help much appreciated!

have a look at your ws section of your browsers network debug tool.

Note: if running locally you will see a ws for the live reload and one for the LV in question.

In your case you may see three ws connections. From your logs it does look like from the csrf token that it maybe making two sockets.

I would start there and look at the messages.

I assume you understand the mounts under normal conditions mount twice because the first is for the http request the second is for the ws request. LiveView: Double Mount | Benjamin Milde

That said, I’m unfamiliar with a LV dead view so in that context I don’t know if its assumed to mount twice.
I just figured to give guidance as to where to look for helping to debug. The network tab in your browser is your friend in most of these cases.

The last time this happened to me (and was driving me nuts) it turned out to be another copy of the app open in another tab :sweat_smile:

5 Likes

I hate to admit how many times this has happened to me too.

4 Likes

Thanks for that. Yeah understand the mount lifecycle, just can’t work out why it goes again. Seem to have two active ws connections on the page.

Sadly have turned off tailscale and double checked all my browsers but still doing it on one tab.

When I said dead, i just meant after the first, non-ws render.

That’s so strange… I’ve been through this exact same thing a few times and it’s always been the “other tab”!

As a Hail Mary to completely rule “some other process” out, I would tried $ lsof -i :4000 juuuuust in case. Or, you know, turn your computer off and on again if you haven’t done so yet :wink:

Actually, revising this…after this I tested with one tab on prod it worked as expected, opened a second and it requested the mount twice. When I closed the tab, it still ran the mount twice. Can someone explain this? obviously I’m getting a hard public lesson in how liveview works in real time :smiling_face_with_tear:

I had a random thought that you might have a duplicated line in your router. I tested out this theory and it does indeed cause your issue! So maybe this is it?

  live "/", SomethingLive, :index
  live "/", SomethingLive, :index

Sorry, just revised my last post. Router as expected.

Check that the JavaScript itself isn’t being loaded and run on the page twice.