Skip to content

Conversation

@evan-hines-js
Copy link
Contributor

@evan-hines-js evan-hines-js commented Dec 13, 2025

The join_ref is currently nil when receiving messages in a client -> server flow. Without the join_ref, the server cannot properly route the message to the correct channel process, causing client->server messages to be silently dropped while server->client messages work fine.

The fix adds join_ref from state.joins[topic], matching the pattern already used in the LeaveTopic handler.

This randomly started happening in several of my projects in the last week without explicit code changes so seems to have been added in 1.2.1 during the struct refactor although I have not investigated deeply.

When pushing messages to a Phoenix channel, the V2 JSON serializer
expects a join_ref to be included in the message array:
[join_ref, ref, topic, event, payload]

Without the join_ref, the server cannot properly route the message
to the correct channel process, causing client->server messages to
be silently dropped while server->client messages work fine.

The fix adds join_ref from state.joins[topic], matching the pattern
already used in the LeaveTopic handler.
@the-mikedavis
Copy link
Collaborator

It looks like #82 didn't touch this block so I'm not sure it's related. Do you have a reproduction case for the dropped messages? Ideally we can add a test case for this bug

@evan-hines-js
Copy link
Contributor Author

evan-hines-js commented Dec 14, 2025

Maybe I am using the library incorrectly but here is a reproduction:

https://github.com/evan-hines-js/slipstream_repro

main branch uses my fork, no_fork branch uses the official release

Run the docker compose for the postgres db and then run the server. On the main branch I see

[info] [PingClient] Sending ping with timestamp: 1765734364541
[info] [PingChannel] Received ping: %{"timestamp" => 1765734364541}
[debug] HANDLED ping INCOMING ON ping:lobby (SlipstreamReproWeb.PingChannel) in 143µs
  Parameters: %{"timestamp" => 1765734364541}
[info] [PingClient] Got reply for ref "2": {:ok, %{"message" => "pong", "timestamp" => 1765734364542}}
[info] [PingClient] Sending ping with timestamp: 1765734365541
[info] [PingChannel] Received ping: %{"timestamp" => 1765734365541}
[debug] HANDLED ping INCOMING ON ping:lobby (SlipstreamReproWeb.PingChannel) in 139µs
  Parameters: %{"timestamp" => 1765734365541}
[info] [PingClient] Got reply for ref "3": {:ok, %{"message" => "pong", "timestamp" => 1765734365542}}
[info] [PingClient] Sending ping with timestamp: 1765734366542
[info] [PingChannel] Received ping: %{"timestamp" => 1765734366542}
[debug] HANDLED ping INCOMING ON ping:lobby (SlipstreamReproWeb.PingChannel) in 75µs
  Parameters: %{"timestamp" => 1765734366542}
[info] [PingClient] Got reply for ref "4": {:ok, %{"message" => "pong", "timestamp" => 1765734366543}}
[info] [PingClient] Sending ping with timestamp: 1765734367543
[info] [PingChannel] Received ping: %{"timestamp" => 1765734367543}
[debug] HANDLED ping INCOMING ON ping:lobby (SlipstreamReproWeb.PingChannel) in 107µs
  Parameters: %{"timestamp" => 1765734367543}
[info] [PingClient] Got reply for ref "5": {:ok, %{"message" => "pong", "timestamp" => 1765734367544}}
[info] [PingClient] Sending ping with timestamp: 1765734368545
[info] [PingChannel] Received ping: %{"timestamp" => 1765734368545}
[debug] HANDLED ping INCOMING ON ping:lobby (SlipstreamReproWeb.PingChannel) in 157µs
  Parameters: %{"timestamp" => 1765734368545}
[info] [PingClient] Got reply for ref "6": {:ok, %{"message" => "pong", "timestamp" => 1765734368546}}
[info] [PingClient] Sending ping with timestamp: 1765734369545
[info] [PingChannel] Received ping: %{"timestamp" => 1765734369545}
[debug] HANDLED ping INCOMING ON ping:lobby (SlipstreamReproWeb.PingChannel) in 36µs
  Parameters: %{"timestamp" => 1765734369545}
[info] [PingClient] Got reply for ref "7": {:ok, %{"message" => "pong", "timestamp" => 1765734369546}}

On the no_fork branch, all the messages are lost:

[info] [PingClient] Sending ping with timestamp: 1765734482018
[info] [PingClient] Sending ping with timestamp: 1765734483019
[info] [PingClient] Sending ping with timestamp: 1765734484020
[info] [PingClient] Sending ping with timestamp: 1765734485021
[info] [PingClient] Sending ping with timestamp: 1765734486022

@evan-hines-js
Copy link
Contributor Author

evan-hines-js commented Dec 14, 2025

With this reproduction repo, I went back through the Slipstream versions and none of them seem to work. Which is highly unusual because my projects just randomly stopped working without this fork even when I went back 10-20 commits (multiple days) in my own repo.

Edit: Found it. Added a new branch to the repro repo no_fork_1.8.2. The issue only occurs with Phoenix 1.8.3 which, according to the changelog has a bug fix that affected how stale join_refs are handled. Now they are dropped.

https://hexdocs.pm/phoenix/changelog.html

So I'm not sure if this change is needed as-is or something that needs to be investigated further. For now I'll pin Phoenix or use my fork.

@the-mikedavis
Copy link
Collaborator

Ah ok, that makes sense that it was a change in Phoenix instead. If I mix deps.update phoenix locally the test suite has many failures, and then with this change everything is passing again.

@the-mikedavis the-mikedavis merged commit 63d30b8 into CuatroElixir:main Dec 15, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants