Troubleshooting
Copy MarkdownConnection 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:
access_token_fnresult (if configured and returns{:ok, token}).client.access_tokenfrom the struct.client.apikeyas a last resort.
Missing Events
Subscribed but not receiving events
Causes:
- Channel not joined yet. Subscriptions only work after the channel reaches
the
:joinedstate. - Wrong topic or filter. Filters are case-sensitive and must match exactly.
- The
handle_event/1callback 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: :okAlso 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: truewas 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.