getsentry / getsentry/sentry-ruby
ActionCable instrumentation breaks all `/cable` connections on Rails 8.2 / main (private handle_open called publicly by Server::Socket)
- Dominant language
- Ruby
- Stars
- 987
- Forks
- 541
- Avg merge
- 17h 40m
- Merged PRs (30d)
- 19
Description
## Issue Description
`sentry-rails` instruments ActionCable by prepending `Sentry::Rails::ActionCableExtensions::Connection` onto the connection, and that module declares `#handle_open` / `#handle_close` as **private** (`sentry-rails/lib/sentry/rails/action_cable.rb`):
```ruby
module Connection
private
def handle_open
ErrorHandler.capture(self, transaction_name: "#{self.class.name}#connect") { super }
end
def handle_close
ErrorHandler.capture(self, transaction_name: "#{self.class.name}#disconnect") { super }
end
end
```
Rails 8.2 / `main` decoupled the cable socket from the connection (rails/rails#50979, merged 2026-05-28). `ActionCable::Server::Socket` now calls the connection back **with an explicit receiver**, which requires the method to be public:
```ruby
def handle_open
@protocol = websocket.protocol
@connection.handle_open # explicit receiver -> must be public
message_buffer.process!
server.add_connection(@connection)
end
```
Because the prepended module sits ahead of `ActionCable::Connection::Base` in the ancestor chain and marks the method private, every `/cable` upgrade raises `NoMethodError`. The WebSocket upgrade returns 101, but `handle_open` raises before `welcome` is sent, so clients never receive `welcome`/pings and reconnect-loop forever — no broadcasts (Turbo Streams, presence) are ever delivered. The exception only reaches the Rails logger, not Sentry, since it happens inside the instrumentation wrapper. In Rails' own `Connection::Base`, both methods are public; the prepended private versions invert the intended visibility.
## Reproduction Steps
1. Rails app tracking `rails/rails` `main` (any revision including the `ActionCable::Server::Socket` extraction; reproduced on 8.2.0.alpha).
2. Add `sentry-rails` and configure `Sentry.init`.
3. Open a WebSocket to `/cable` (e.g. a Turbo Stream subscription).
4. The upgrade raises immediately:
```
NoMethodError: private method 'handle_open' called for an instance of ApplicationCable::Connection
actioncable/lib/action_cable/server/socket.rb:127:in 'handle_open'
```
## Expected Behavior
`/cable` WebSocket connections open normally with Sentry instrumentation active.
## Actual Behavior
Every `/cable` upgrade raises `NoMethodError (private method 'handle_open' called ...)`; the client never receives `welcome` and reconnect-loops; no broadcasts are delivered.
## Ruby Version
4.0.1
## SDK Version
sentry-rails 6.6.1 (also reproduces on the latest release 6.6.2 and current master — both still mark these private)
## Integration and Its Version
Rails 8.2.0.alpha (rails/rails main, rev 324b65c2dffe17ee03522b39b516f482087f244f) — ActionCable
## Sentry Config
Standard `Sentry.init`; no special config needed to trigger.
## Workaround (until a fix ships)
Re-publicize the two methods in an initializer:
```ruby
Sentry::Rails::ActionCableExtensions::Connection.send(:public, :handle_open, :handle_close)
```
Contributor guide
Assessment
This issue has not been assessed yet.