How do you implement a heartbeat in a WebSockex client without hitting CallingSelfError

I’m building a simple WebSocket client using the WebSockex library. I want to add a basic heartbeat mechanism to keep the connection alive and detect disconnects. Since the client is pretty simple, I was thinking of sending a ping message every few seconds like this:


  @impl true
  def handle_connect(_conn, state) do
    IO.puts("Connected ....")
    Process.send_after(self(), :heartbeat, 10_000)
    {:ok, state}
  end

  def handle_info(:heartbeat, state) do
    pid = WebSockex.Utils.whereis(...)
    WebSockex.send_frame(pid, {:text, "ping"})

    Process.send_after(self(), :heartbeat, 10_000)
    {:ok, state}
  end

This crashes with:

shell process exited with reason: {%WebSockex.CallingSelfError{function: :send_frame}, []}

However, it works fine if I extract the ping logic to a separate function and call it manually.

So my questions are:

  • Why does calling WebSockex.send_frame(...) directly from handle_info cause this crash?
  • What’s the idiomatic way to implement a heartbeat in WebSockex?
  • How do you personally structure your WebSockex heartbeat logic?

Any pointers or code examples would be appreciated.

Hi, @csokun :wave:

It seems like the full error message suggests:

Instead try returning {:reply, frame, state} from the callback.

here

So maybe try

  def handle_info(:heartbeat, state) do
    Process.send_after(self(), :heartbeat, 10_000)
    {:reply, {:text, "ping"}, state}
  end

?

I haven’t used WebSockex, just in case =)

4 Likes

OMG! You nailed it! :pray:

2 Likes