getsentry / getsentry/sentry-ruby

ActionCable instrumentation breaks all `/cable` connections on Rails 8.2 / main (private handle_open called publicly by Server::Socket)

Open
#2,975 2 comments 0 reactions 1 assignee Claimed by @solnic View on GitHub
Bug Ruby Waiting for: Product Owner
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.