## TL;DR
Realtime replays table changes through Row Level Security, so a table with RLS enabled and no SELECT policy for your client's role rejects the subscription. Add the policy, add the table to the `supabase_realtime` publication, and resubscribe.

## Error
```text
channel error: subscription failed  -  realtime subscription rejected (RLS policy check)
```

## Steps

1. Confirm the channel setup matches the table exactly: table name, schema, and any `postgres_changes` filter. A typo here fails the same way.
Expected output: you rule out naming mistakes before touching policies.

2. Add a SELECT policy for the role your client uses (`anon` for public clients, `authenticated` for logged-in users):
```sql
CREATE POLICY "public read" ON [your_table]
FOR SELECT TO anon USING (true);
```
Expected output: the role can read the table, which is what the realtime subscription checks.

3. Add the table to the realtime publication:
```sql
ALTER PUBLICATION supabase_realtime ADD TABLE [your_table];
```
Expected output: `ALTER PUBLICATION` succeeds. Tables not in this publication never emit realtime events.

4. Resubscribe and check the channel status reaches joined/subscribed instead of erroring.
Expected output: inserts on the table now arrive on the channel.

## When to use
- The channel errors right after joining
- Realtime worked before someone enabled RLS on the table
- You are using the anon key or a logged-in user and reads work via the API but not via realtime

## When not to use
- Websocket connection failures or timeouts (thats network, not RLS)
- Events arrive but with stale data (thats a publication or filter issue)
- You want realtime without any read access at all (RLS still applies; there is no bypass for subscriptions)

## Variant phrasings

### postgres_changes filter gets no events
The filter (event type, schema, table, column filter) must match the change exactly. An overly narrow filter looks identical to a broken subscription.

### realtime works for postgres public schema tables but not private ones
Private-schema tables need both the RLS policy and the publication entry; the dashboard toggle only covers `public`.

## Why it happens
Supabase Realtime does not bypass RLS: each change is re-checked against the subscribing role's policies before delivery. Enabling RLS without a matching SELECT policy turns every subscription into a rejection, which the client surfaces as a channel error.

## Edge cases
- Policies using `auth.uid()` only work for the `authenticated` role; the anon key never satisfies them.
- RLS enabled with zero policies denies everything, which is the most common way this breaks right after "securing" a table.
- Updates and deletes also need the table's replica identity set, or realtime cannot deliver the old row.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_pj7dF4CUUrKf_sPbj47eew
