# Debug Supabase Realtime from publication to browser

> A connected Supabase channel can still deliver nothing. Check publication membership, RLS, event requirements, and the browser subscription lifecycle.

Source: https://saascode.ai/inside/realtime-publication · Published: 2026-08-21 · Section: academy

---
The most expensive Realtime failure is a subscription that appears connected and never fires. There is no obvious crash. The screen is simply stale.

Every Realtime feature in one live build failed that way. The initial diagnosis bundled three requirements together, but current Supabase behavior makes an important distinction: publication membership is required for Postgres Changes; `REPLICA IDENTITY FULL` is conditional; and the client must live in a runtime that can keep the subscription alive.

Debug those layers in that order.

## 1. Is the table being published?

For Postgres Changes, enable the table under the `supabase_realtime` publication or add it in a migration:

```sql
alter publication supabase_realtime
  add table public.messages;
```

Make that migration idempotent in the style used by your project, and avoid adding the same table twice. Supabase now recommends Broadcast for many scalable database-change use cases, but Postgres Changes remains the simpler option for modest workloads.

## 2. May this subscriber read the row?

With RLS enabled, Realtime only sends Postgres Changes records a subscriber is allowed to read. Confirm the client has a current session, the table grants include `SELECT` for the relevant role, and the RLS policy permits the row.

Test with two users from different tenants. “The owner receives the event” and “the other tenant does not” are both required assertions.

Do not add `REPLICA IDENTITY FULL` automatically. Use it when you need old row values on updates or deletes, or when your delete-filter behavior requires it:

```sql
alter table public.messages replica identity full;
```

Full replica identity increases the data written to the WAL. It is a tradeoff, not a universal setup step.

## 3. Does the subscription survive?

Create and subscribe to the channel in the browser or another long-lived runtime. A request-scoped server client cannot keep a WebSocket alive after the request ends. The package name alone does not decide this: `@supabase/ssr` provides both server and browser client factories, and a browser client ultimately uses `supabase-js` Realtime.

In React, subscribe inside an effect and remove the channel on cleanup. Log channel status while debugging, and surface a degraded state instead of pretending stale data is current.

## Prove the entire path

Insert a known row after the channel reports subscribed. Record whether the database change occurred, the row satisfied RLS for the subscriber, the table belonged to the publication, and the callback ran once. Repeat for `UPDATE` and `DELETE` only if the feature needs them.

This turns “Realtime is broken” into four smaller questions: was the change written, published, authorized, and received? The original build missed several layers at once. Your diagnostic should make each layer independently visible.
