# Debugging Realtime with logs and DevTools

> How to debug Supabase Realtime on SupaHost from the client side: subscribe errors, the client logger, and reading WebSocket frames in browser DevTools, since there is no Realtime log viewer yet.

Source: https://supahost.app/kb/realtime-debugging · Updated 2026-11-11

## What you see

Something in Realtime isn't working (a channel won't join, messages go missing, connections drop) and you need to find out why.

## Why it's harder than it looks

Realtime errors mostly happen on the server, during a channel join or while delivering a message. The client only learns about them through status callbacks and WebSocket frames. So the fastest way to debug is to make the client tell you everything it hears.

## How to debug it

1. **Log the full subscribe error.** The second argument carries the reason, often with a code such as `InvalidJWTExpiration` or `ChannelRateLimitReached`:

   ```js
   supabase.channel('recipe-comments:88').subscribe((status, err) => {
     console.log('[realtime]', status, err ?? '')
   })
   ```

   Log the whole `err`, not just `err.message`; its `cause` often holds the detail.

2. **Turn on the client logger.** It prints every push, reply and reconnect:

   ```js
   const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
     realtime: {
       logger: (kind, msg, data) => console.debug(`[rt:${kind}]`, msg, data),
     },
   })
   ```

   Use it in development only; it's noisy.

3. **Read the WebSocket frames.** In Chrome or Edge: DevTools → **Network** → filter **WS** → click the `realtime/v1/websocket` request → **Messages**. In Firefox the tab is called **Response**. Look for:

   - `phx_join` (your join request) and its `phx_reply`. A reply with `"status":"error"` has the reason in `response.reason`.
   - `system` events on a channel, which report server-side problems such as rate limits.
   - `heartbeat` every 25 seconds. Gaps mean the tab was throttled or the network dropped.
   - The close frame, if the server ended the connection.

   If there's no `101 Switching Protocols` status on the request at all, the WebSocket never opened: check the URL, the key, and any proxy or VPN.

4. **Check the database side.** For Postgres Changes, confirm the table is in the publication and the user can `select` the row. In Studio's SQL editor:

   ```sql
   select tablename from pg_publication_tables where pubname = 'supabase_realtime';
   ```

5. **Reduce to the smallest case.** Two browser tabs, one channel, one event name. Once that works, add authorization, filters and your app code back one at a time.

## On SupaHost

The SupaHost dashboard doesn't have a Realtime log viewer. The project **Logs** tab is coming soon, and Studio's Logs pages aren't available. Realtime is also not one of the services covered by the `sudo supahost-logs` command on Business plans. So client-side debugging (steps 1 to 3) is the way to go.

The Realtime `log_level` connection parameter you may see in other guides only changes server-side logging, so it doesn't help here.

If you're still stuck, email support with the project name, the channel topic, the exact `reason` string and the time it happened. See [Writing a support request that gets solved fast](/kb/support-requests).

## Related

- [Realtime TIMED_OUT and CHANNEL_ERROR](/kb/realtime-timed-out)
- [Realtime messages not arriving](/kb/realtime-messages-not-arriving)
- [Understanding Realtime heartbeats](/kb/realtime-heartbeats)
- [Studio Logs and Reports pages are missing](/kb/studio-logs-reports-missing)
