Skip to content

parity(realtime): block postgres_changes listener registration after Subscribe() [from supabase-js] #62

Description

@grdsdev

SDK Parity: C# implementation needed

A change was made in `supabase-js` that needs to be implemented in this repository for SDK parity.

Reference Implementation (supabase-js)

What Changed

The `RealtimeChannel.on()` method was updated to throw an error when attempting to add a `postgres_changes` listener after calling `subscribe()`. Previously, only `presence` listeners were blocked, and only in the `joined` state. The fix:

  1. Blocks `postgres_changes` listener registration (in addition to `presence`)
  2. Blocks in both the `joining` and `joined` states (not just `joined`)
  3. Updates the error message to include the listener type and channel topic

Code Reference

// Before:
if (this.channelAdapter.isJoined() && type === REALTIME_LISTEN_TYPES.PRESENCE) {
  throw new Error('cannot add presence callbacks after joining a channel')
}

// After:
const stateCheck = this.channelAdapter.isJoined() || this.channelAdapter.isJoining()
const typeCheck =
  type === REALTIME_LISTEN_TYPES.PRESENCE || type === REALTIME_LISTEN_TYPES.POSTGRES_CHANGES

if (stateCheck && typeCheck) {
  throw new Error(`cannot add \`${type}\` callbacks for ${this.topic} after \`subscribe()\`.`)
}

Implementation Guidance

Current State in realtime-csharp

The current Register(PostgresChangesOptions) and AddPostgresChangeHandler() methods in RealtimeChannel.cs do not check the channel's subscription state:

public IRealtimeChannel Register(PostgresChangesOptions postgresChangesOptions)
{
    PostgresChangesOptions.Add(postgresChangesOptions);
    // No guard against calling after Subscribe()
}

public void AddPostgresChangeHandler(ListenType listenType, PostgresChangesHandler postgresChangeHandler)
{
    BindPostgresChangesHandler(listenType, postgresChangeHandler);
    // No guard against calling after Subscribe()
}

The channel has IsJoining and IsJoined-equivalent properties available via State.

Expected Behavior

When a user calls Register(PostgresChangesOptions) or AddPostgresChangeHandler() on a channel that is already in Joining or Joined state, the SDK should throw an InvalidOperationException with a clear error message.

Key Behaviors to Match

  • Throw InvalidOperationException when State == ChannelState.Joining || State == ChannelState.Joined
  • Error message should identify the operation and channel topic
  • Non-postgres/presence operations should not be affected
  • Registration before Subscribe() continues to work normally

Files Likely Affected

  • Realtime/RealtimeChannel.cs

Acceptance Criteria

  • Calling Register(PostgresChangesOptions) after Subscribe() throws InvalidOperationException
  • Calling AddPostgresChangeHandler() after Subscribe() throws InvalidOperationException
  • Exception is thrown in both Joining and Joined states
  • Non-postgres listeners are not affected by this guard
  • Unit tests cover the throwing cases
  • No breaking changes to registering postgres_changes before Subscribe()

Context


Generated with Claude Code /sync-sdk-parity

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions