Troubleshooting

Copy Markdown

Connection Issues

WebSocket fails to connect

Symptoms: Logs show repeated "Failed to connect" messages.

Causes:

  • Wrong project URL or API key in your %Supabase.Client{}.
  • Network issues between your server and Supabase.
  • Firewall blocking WebSocket (port 443) connections.

Fix: Verify your client config:

{:ok, client} = Supabase.init_client("https://your-project.supabase.co", "your-api-key")
IO.inspect(client.realtime_url)

The realtime_url should point to your project's realtime endpoint.

Connection keeps dropping

Symptoms: Frequent :gun_down messages in logs followed by reconnection.

Causes:

  • Heartbeat interval too long, causing the server to close idle connections.
  • Network instability.

Fix: Lower the heartbeat interval:

{MyApp.Realtime, client: client, heartbeat_interval: :timer.seconds(15)}

The heartbeat tells the server the client is still alive. If the server does not receive a heartbeat within its timeout, it closes the connection.

Authentication Failures

"unauthorized" or 401 errors

Causes:

  • Expired or invalid API key.
  • Expired access token.
  • No token refresh function configured.

Fix: Provide an access_token_fn to refresh tokens:

{MyApp.Realtime,
  client: client,
  access_token_fn: fn -> MyApp.Auth.get_fresh_token() end}

The token resolution order is:

  1. access_token_fn result (if configured and returns {:ok, token}).
  2. client.access_token from the struct.
  3. client.apikey as a last resort.

Missing Events

Subscribed but not receiving events

Causes:

  • Channel not joined yet. Subscriptions only work after the channel reaches the :joined state.
  • Wrong topic or filter. Filters are case-sensitive and must match exactly.
  • The handle_event/1 callback does not match the event shape.
  • Replication not enabled on the table in your Supabase dashboard.

Fix: Check the channel state and your subscription:

{:ok, channel} = MyApp.Realtime.channel("public:users")
:ok = MyApp.Realtime.on(channel, "postgres_changes",
  event: :insert, schema: "public", table: "users"
)

Make sure your callback matches the event tuple:

# This matches INSERT events
def handle_event({:postgres_changes, :insert, payload}), do: :ok

# This matches all database events
def handle_event({:postgres_changes, _operation, _payload}), do: :ok

Also verify that Realtime replication is enabled for the table in your Supabase project dashboard under Database > Replication.

Broadcast events not received

Causes:

  • Not subscribed to the broadcast event name.
  • Using broadcast: [self: false] (the default) and sending to yourself.

Fix: Subscribe with the exact event name:

:ok = MyApp.Realtime.on(channel, "broadcast", event: "my_event")

Or use a wildcard to catch all events:

:ok = MyApp.Realtime.on(channel, "broadcast", event: "*")

To receive your own broadcasts, enable self:

{:ok, channel} = MyApp.Realtime.channel("room", broadcast: [self: true])

Buffer Overflow

Messages dropped while disconnected

Symptoms: Some messages sent during a disconnection are lost.

Why: The send buffer has a maximum size of 100 entries. When full, the oldest messages are dropped to make room for new ones.

Fix: For important messages, use the HTTP fallback:

{MyApp.Realtime, client: client, http_fallback: true}

With HTTP fallback enabled, broadcast messages are sent through the REST API when the WebSocket is down. Other message types (presence, postgres_changes) are still buffered.

HTTP Fallback

HTTP fallback not working

Causes:

  • http_fallback: true was not set.
  • The message is not a broadcast. Only broadcast messages use the HTTP fallback.
  • Network issues reaching the Supabase REST API.

Fix: Make sure you enable it:

{MyApp.Realtime, client: client, http_fallback: true}

Check logs for "[Supabase.Realtime.HTTP]: HTTP broadcast failed" messages.

HTTP fallback succeeds but events not received

Why: HTTP fallback delivers the broadcast to the server, but other clients need an active WebSocket connection to receive it. The fallback only helps with sending, not receiving.