Skip to content

Throw 90011 when releasing a realtime channel that isn't detached (RTS4c-e) - #1358

Merged
SimonWoolf merged 1 commit into
integration/v2from
release-throw-unless-detached
Oct 9, 2026
Merged

SimonWoolf merged 1 commit into
integration/v2from
release-throw-unless-detached

Conversation

@SimonWoolf

Copy link
Copy Markdown
Member

Implements RTS4c–e from spec 6.3.0 (ably/specification#557) for 2.0.

Breaking change: Channels.Release(name) on a realtime channel that isn't Initialized, Detached or Failed no longer detaches it implicitly. It throws an AblyException with code 90011 and status 400, and leaves the channel untouched. Channels.ReleaseAll() checks every channel first, and throws without releasing anything if any channel is in another state. Releasing an unknown name is still a no-op that returns false. A channel that can be released is removed before Release returns.

Migration: detach first and wait for it to complete, then release:

await channel.DetachAsync();
realtime.Channels.Release(channel.Name);

UPDATING.md documents this.

Other changes:

  • Adds ErrorCodes.ChannelReleaseInvalidState = 90011 (registered in errors: Add 90011 "Channel released while not detached" ably-common#367).
  • Client dispose clears the channel collection directly instead of via ReleaseAll(), so it can't throw.
  • Replaces the RTS4a unit tests with UTS-derived RTS4c/RTS4d/RTS4e tests and ReleaseAll tests.
  • Updates the sandbox ReleaseAll test to detach first.

The current-major deprecation is in #1357. Reference ably-js PRs: ably/ably-pubsub-js#2322 (deprecation) and ably/ably-pubsub-js#2323 (next major).

🤖 Generated with Claude Code

…S4c-e)

Spec 6.3.0 replaces RTS4a, under which Channels.Release() detached a
channel not in the Initialized, Detached or Failed state and removed it
once the detach completed, with RTS4c-e: Release() removes such a channel
synchronously, is a no-op for an unknown name, and for any other state
throws 90011/400 and does nothing else. Detaching implicitly let a
released channel keep receiving server updates until the DETACHED
arrived, and hid detach failures from the caller.

ReleaseAll() checks every channel before releasing any, so it either
releases all of them or throws without changing anything. Disposing the
client now clears the channel collection directly instead of going
through ReleaseAll(), which would otherwise throw from the workflow for
channels still attached when the connection closes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 954d4ff8-306f-46d0-a2dc-40d9f5b6e392
📥 Commits

Reviewing files that changed from the base of the PR and between a12cbb9 and 1f9939e.

📒 Files selected for processing (6)
  • UPDATING.md
  • src/Ably.PubSub.Shared/Realtime/RealtimeChannels.cs
  • src/Ably.PubSub.Shared/Realtime/Workflows/RealtimeWorkflow.cs
  • src/Ably.PubSub.Shared/Types/ErrorCodes.cs
  • src/Ably.PubSub.Tests.Shared/Realtime/ChannelSandboxSpecs.cs
  • src/Ably.PubSub.Tests.Shared/Realtime/ChannelsSpecs.cs
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ttypic ttypic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Release validation and removal are not synchronized with concurrent channel state transitions.

1 open finding
What changed in this PR

Implements strict realtime channel release behavior for 2.0, including error 90011 and disposal-safe cleanup.

Changes:

  • Rejects release of channels in non-releasable states.
  • Updates ReleaseAll() and disposal cleanup.
  • Adds migration documentation and RTS4c–e test coverage.
File Summary
UPDATING.md Documents the breaking release behavior.
src/​Ably.PubSub.Tests.Shared/​Realtime/​ChannelsSpecs.cs Adds release behavior tests.
src/​Ably.PubSub.Tests.Shared/​Realtime/​ChannelSandboxSpecs.cs Detaches channels before release.
src/​Ably.PubSub.Shared/​Types/​ErrorCodes.cs Adds error code 90011.
src/​Ably.PubSub.Shared/​Realtime/​Workflows/​RealtimeWorkflow.cs Updates cleanup behavior.
src/​Ably.PubSub.Shared/​Realtime/​RealtimeChannels.cs Implements state validation and release logic.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +116 to +119
ThrowIfNotReleasable(channel);

RemoveChannel();
}
// RTS4d
return RemoveChannel(name);
@SimonWoolf
SimonWoolf merged commit 487f077 into integration/v2 Oct 9, 2026
18 checks passed
@SimonWoolf
SimonWoolf deleted the release-throw-unless-detached branch October 9, 2026 16:15

This branch was successfully deployed

1 active deployment
staging/pull/1358/features — 1f9939e7 Deployed Oct 9, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants