Fix Turbo::StreamsChannel::Error: Turbo::StreamsChannel refused connection for unauthorized user in Rails
This error occurs when a Turbo Streams broadcast channel refuses the WebSocket connection because the user is not authorized. Hotwire uses ActionCable for real-time updates and requires proper channel authorization. Ensure your Turbo::StreamsChannel subscription is verified and your signed stream names match between the broadcast and subscription.
Reading the Stack Trace
Here's what each line means:
- turbo-rails (1.5.0) app/channels/turbo/streams_channel.rb:18:in `subscribed': Turbo Streams channel rejects the subscription because the signed stream name verification failed.
- actioncable (7.1.3) lib/action_cable/channel/base.rb:146:in `subscribe_to_channel': ActionCable processes the subscription request and the channel rejects it.
- app/views/posts/show.html.erb:3:in `_app_views_posts_show_html_erb__render': The view uses turbo_stream_from which initiates the WebSocket subscription.
Common Causes
1. Mismatched stream name signing
The broadcast uses a different stream name than the one the client subscribed to.
# View subscribes to:
<%= turbo_stream_from @post %>
# But broadcast sends to a different stream:
Turbo::StreamsChannel.broadcast_append_to(
'all_posts', # Different from @post stream
target: 'comments',
partial: 'comments/comment'
)
2. ActionCable connection not authenticated
The ApplicationCable::Connection does not authenticate the user for Turbo Streams.
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
# No authentication - current_user is nil
end
end
3. Missing turbo-rails gem configuration
The turbo-rails gem is not properly configured for broadcasts.
# Gemfile includes turbo-rails but cable.yml uses async adapter
# config/cable.yml
production:
adapter: async # Broadcasts won't work across processes
The Fix
Ensure the turbo_stream_from helper and the broadcast target use the same streamable object. Using the model instance directly ensures the signed stream names match between the subscription and the broadcast.
<%= turbo_stream_from 'all_posts' %>
# Broadcasting to mismatched stream:
Turbo::StreamsChannel.broadcast_append_to(
@post,
target: 'comments',
partial: 'comments/comment'
)
<%= turbo_stream_from @post %>
# Model with broadcasts:
class Comment < ApplicationRecord
belongs_to :post
after_create_commit -> {
broadcast_append_to post, target: 'comments',
partial: 'comments/comment'
}
end
Testing the Fix
require 'rails_helper'
RSpec.describe 'Turbo Stream broadcasts', type: :system do
let(:post) { create(:post) }
it 'broadcasts new comments to the post stream' do
visit post_path(post)
expect(page).to have_css('#comments')
comment = create(:comment, post: post, body: 'Live comment!')
expect(page).to have_content('Live comment!')
end
end
RSpec.describe Comment, type: :model do
it 'broadcasts after creation' do
post = create(:post)
expect {
create(:comment, post: post, body: 'Test')
}.to have_broadcasted_to(post)
end
end
Run your tests:
bundle exec rspec spec/models/comment_spec.rb spec/system/turbo_stream_spec.rb
Pushing Through CI/CD
git checkout -b fix/rails-hotwire-broadcast,git add app/models/comment.rb app/views/posts/show.html.erb,git commit -m "fix: align Turbo Stream broadcast and subscription targets",git push origin fix/rails-hotwire-broadcast
Your CI config should look something like this:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
ports: ['5432:5432']
redis:
image: redis:7
ports: ['6379:6379']
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true
- run: bin/rails db:setup
- run: bundle exec rspec
The Full Manual Process: 18 Steps
Here's every step you just went through to fix this one bug:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- Monitor production to confirm the error is resolved
Total time: 30-60 minutes. For one bug.
Or Let bugstack Fix It in Under 2 minutes
Every step above? bugstack does it automatically.
Step 1: Install the SDK
gem install bugstack
Step 2: Initialize
require 'bugstack'
Bugstack.init(api_key: ENV['BUGSTACK_API_KEY'])
Step 3: There is no step 3.
bugstack handles everything from here:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- Deploys to production (or opens a PR for review)
Time from error to fix deployed: Under 2 minutes.
Human involvement: zero.
Try bugstack Free →No credit card. 5-minute setup. Cancel anytime.
Deploying the Fix (Manual Path)
- Align broadcast and subscription stream names.
- Configure Redis for ActionCable in production.
- Add system and model specs for broadcasts.
- Open a pull request.
- Merge and verify real-time updates work in staging.
Frequently Asked Questions
BugStack runs the fix through your existing test suite, generates additional edge-case tests, and validates that no other components are affected before marking it safe to deploy.
BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review it before merging.
Turbo Drive and Turbo Frames work without ActionCable. Only Turbo Streams broadcasts require ActionCable for real-time updates over WebSockets.
Check the browser DevTools WebSocket tab for connection errors, verify Redis is running, and check the Rails server logs for ActionCable subscription messages.