Skip to content
Latchkey

Phoenix Channels "phx_error" on the socket in CI

The Phoenix JS/socket protocol sends phx_error when a channel process crashes or the socket transport errors. Instead of a phx_reply with :ok, the client gets a phx_error and the channel is closed.

What this error means

A JS or integration test observes a phx_error message on the channel, or the server logs a channel crash. The channel that was joined stops responding.

Phoenix
[error] GenServer #PID<0.512.0> terminating
** (KeyError) key :user_id not found in: %{}
Last message: %Phoenix.Socket.Message{event: "phx_join", topic: "room:lobby", ...}
push("phx_error", %{})

Common causes

The channel process crashed handling a message

An exception in a channel callback (a missing key, a failed match) terminates the channel process, and Phoenix emits phx_error to the client.

The socket transport errored

A transport-level failure on the socket surfaces to the client as phx_error, closing the affected channels.

How to fix it

Fix the crashing callback

  1. Read the server log line that precedes the phx_error to find the exception.
  2. Handle the missing key or failed match in the channel callback.
  3. Re-run the test and assert a :ok reply instead of an error.
room_channel.ex
def join("room:" <> _id, _params, socket) do
  case socket.assigns[:user_id] do
    nil -> {:error, %{reason: "unauthorized"}}
    _ -> {:ok, socket}
  end
end

Assert the failure explicitly in tests

When an error is expected, assert on it so the test is deterministic.

test.exs
assert {:error, %{reason: "unauthorized"}} =
  subscribe_and_join(socket, "room:lobby", %{})

How to prevent it

  • Guard channel callbacks against missing assigns and bad params.
  • Read the server crash log that precedes any phx_error.
  • Assert expected error replies rather than treating them as flakes.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →