Short answer
First check the table is in the supabase_realtime publication, since Realtime ignores tables that aren’t. Then check row-level security: Realtime applies it as the subscribing user, so on a table with RLS it only sends changes that user is allowed to see, and SUBSCRIBED doesn’t mean they can. If events arrive but old values are empty, that’s replica identity. Last, check the table name, schema and filter in the subscription.
The causes, most common first
01
The table isn’t in the Realtime publication
“Realtime doesn’t watch every table by default. Each one has to be added to a publication called supabase_realtime.” Supabase, Postgres Changes Troubleshooting
Creating a table and turning on Realtime for it are two separate steps. Add the table to the supabase_realtime publication in Database, Publications in the dashboard, then try again.
02
Row-level security hides the row
“SUBSCRIBED only means the WebSocket connected. It says nothing about whether that client is allowed to see the data.” Supabase, Postgres Changes Troubleshooting
Realtime enforces row-level security the way a normal query would, as the subscribing user (deletes are the exception). Sign in as that user and select the changed row; if nothing comes back, the policy is the cause. For updates, the policy generally has to allow both the old and the new version of the row, or the event can fail to deliver.
03
Old values are empty
“If events are arriving but payload.old is mostly empty or null, that’s a replica identity problem, not a delivery problem.” Supabase, Postgres Changes Troubleshooting
If events arrive but the old record only has the id, Postgres is sending just the primary key. Setting the table’s replica identity to full sends the whole old row, but on a table with row-level security the old record still holds only the primary key, so don’t build logic on it there.
04
The subscription code doesn’t match
Check the table name and schema match exactly, the filter is written like room_id=eq.abc123, and the status callback is logged. In React and Next.js, clean up old channels when a component re-renders, or you’ll get duplicate or stale subscriptions.
Checked against the vendor’s docs on : Supabase: Realtime Postgres Changes troubleshooting; Supabase docs: Postgres Changes.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way.See scope and start
Related: New row violates row-level security · RLS is on: is the data safe now? · Free Supabase RLS policy checker