LiveView.send_update and handle_info

I have a LiveView that implements a handle_info callback but that should also handle a send_update.
That doesn’t seem to work, because I get a function clause error: no function clause matching in ....handle_info/2

It seems that by implementing some handle_info callbacks myself, I break the behaviour of send_update, that expects handle_info callback to be available?

If I remove the handle_info callbacks everything works fine.

Is that expected behaviour, bug or am I doing something wrong?

send_update is for LiveComponents only. handle_info is a callback only for LiveViews.

There’s no API suitable for both liveviews and livecomponents yet:

That’s not the issue (I think). I have a LiveComponent as a child of a LiveView. I want to use send_update to update the LC. But internally that goes through the LiveView.
If that LiveView implements handle_info callbacks, the above error occurs.

I’m using the send_update example with a pid from a tasks as the example in the docs shows.

Hmm. I’d imagine LV would handle the send_update case internally and not even call your callback handle_info in the first place. If that’s not the case I’d expect this to be a bug.

How exactly? Could it be that you’re calling send_update from a process other than the LV (like a task)? In that case you need to pass the LiveView’s pid to send_update.

Mmh sorry. After rereading your comment and the docs, I realized you’re probably already aware of that :slight_smile:

I’ve set up a minimal reproduction repository: GitHub - tcoopman/bug-send_update: Minimal reproduction of send_update bug?

Have a look a the commits, if this seems a bug to you, I’ll report it.
This commit introduces the send_update that works: minimal working setup · tcoopman/bug-send_update@2c3bd5a · GitHub

This commit introduces the bug: change that breaks send_update · tcoopman/bug-send_update@74756c0 · GitHub

I noticed that before breaking commit (so the liveview without a handle_info in it), I get warnings in my console:

  [debug] HANDLE EVENT
  Component: BugWeb.BugComponent
  View: BugWeb.BugLive
  Event: "trigger"
  Parameters: %{"value" => ""}
[debug] Replied in 158µs
[debug] warning: undefined handle_info in BugWeb.BugLive. Unhandled message: {#Reference<0.2535388494.1662582810.76242>, {:phoenix, :send_update, {BugWeb.BugComponent, "Id", %{id: "Id", status: "ok"}}}}
[debug] warning: undefined handle_info in BugWeb.BugLive. Unhandled message: {:DOWN, #Reference<0.2535388494.1662582810.76242>, :process, #PID<0.696.0>, :normal}

But those warnings become an error when the handle_info is implemented:

[error] GenServer #PID<0.741.0> terminating
** (FunctionClauseError) no function clause matching in BugWeb.BugLive.handle_info/2
    (bug 0.1.0) lib/bug_web/live/bug_live.ex:12: BugWeb.BugLive.handle_info({#Reference<0.2535388494.1662582810.77010>, {:phoenix, :send_update, {BugWeb.BugComponent, "Id", %{id: "Id", status: "ok"}}}}, #Phoenix.LiveView.Socket<id: "phx-FyvUXB_5qTrGPAKE", endpoint: BugWeb.Endpoint, view: BugWeb.BugLive, parent_pid: nil, root_pid: #PID<0.741.0>, router: BugWeb.Router, assigns: %{__changed__: %{}, flash: %{}, live_action: :index}, transport_pid: #PID<0.735.0>, ...>)
    (phoenix_live_view 0.18.3) lib/phoenix_live_view/channel.ex:260: Phoenix.LiveView.Channel.handle_info/2
    (stdlib 4.1.1) gen_server.erl:1123: :gen_server.try_dispatch/4
    (stdlib 4.1.1) gen_server.erl:1200: :gen_server.handle_msg/6
    (stdlib 4.1.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Last message: {#Reference<0.2535388494.1662582810.77010>, {:phoenix, :send_update, {BugWeb.BugComponent, "Id", %{id: "Id", status: "ok"}}}}
State: %{components: {%{1 => {BugWeb.BugComponent, "Id", %{__changed__: %{}, flash: %{}, id: "Id", myself: %Phoenix.LiveComponent.CID{cid: 1}, status: "ok"}, %{__changed__: %{}, root_view: BugWeb.BugLive}, {46570842166237931649808947786033767038, %{1 => {221396633504767734881948039861660186427, %{3 => {256413836812106541222927451835396884738, %{}}}}}}}}, %{BugWeb.BugComponent => %{"Id" => 1}}, 2}, join_ref: "4", serializer: Phoenix.Socket.V2.JSONSerializer, socket: #Phoenix.LiveView.Socket<id: "phx-FyvUXB_5qTrGPAKE", endpoint: BugWeb.Endpoint, view: BugWeb.BugLive, parent_pid: nil, root_pid: #PID<0.741.0>, router: BugWeb.Router, assigns: %{__changed__: %{}, flash: %{}, live_action: :index}, transport_pid: #PID<0.735.0>, ...>, topic: "lv:phx-FyvUXB_5qTrGPAKE", upload_names: %{}, upload_pids: %{}}

To be clear, the send_update through - the live component is rerendered, but after that the liveview crashes.

1 Like

Thanks, it all makes sense now.

The task supervisor will be sending a {task_ref, result} message to your LiveView when your task complets (and also a {:DOWN, task_ref, :process, pid, reason} message in this case, because you’re starting it with async_nolink). You can see these messages in your logged warnings.

Those messages should be handled by a handle_info in your LiveView. If your LiveView doesn’t implement a handle_info callback, a warning will be logged. But if it does implement a handle_info callback, that callback will be called, and if there is no matching clause, your LiveView will crash.

Adding a catchall handle_info(_, socket), do: {:noreply, socket} will solve your immediate problem here.

1 Like

That’s so obvious. How did I not see that.

Thanks :heart:

1 Like

Here’s the documentation of those messages: Task — Elixir v1.14.2

1 Like