UncaughtThrowError(uncaught throw :warden) when using timeoutable with ApplicationCable
Author: reinislaCreated Nov 8, 2021Updated Mar 12, 2026
Same issue as UncaughtThrowError warden #4427
When ApplicationCable Subscription to channel is active and timeout_in is set to x minutes, when the timeout kicks in ApplicationCable will try to stream to channel, trigger find_verified_user and fail with UncaughtThrowError(uncaught throw :warden)
Environment
- Ruby [3.0.2]
- Rails [6.1.4]
- Devise [4.8.0]
devise.rb
config.timeout_in = 20.minutesCurrent behavior
Default setup for ApplicationCable Connection
# frozen_string_literal: true
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private def find_verified_user
env["warden"].user or reject_unauthorized_connection
end
end
endIf you have an active channel subscription and Devise timeout kicks in
env["warden"].userwill fail with
↳ app/channels/application_cable/connection.rb:12:in `find_verified_user'
There was an exception - UncaughtThrowError(uncaught throw :warden)
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/devise-4.8.0/lib/devise/hooks/timeoutable.rb:28:in `throw'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/devise-4.8.0/lib/devise/hooks/timeoutable.rb:28:in `block in <main>'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/warden-1.2.9/lib/warden/hooks.rb:15:in `block in _run_callbacks'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/warden-1.2.9/lib/warden/hooks.rb:10:in `each'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/warden-1.2.9/lib/warden/hooks.rb:10:in `_run_callbacks'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/warden-1.2.9/lib/warden/manager.rb:52:in `_run_callbacks'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/warden-1.2.9/lib/warden/proxy.rb:191:in `set_user'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/warden-1.2.9/lib/warden/proxy.rb:229:in `user'
/Users/reinis/sites/abtion/bladkompagniet/app/channels/application_cable/connection.rb:12:in `find_verified_user'
/Users/reinis/sites/abtion/bladkompagniet/app/channels/application_cable/connection.rb:8:in `connect'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/connection/base.rb:173:in `handle_open'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/server/worker.rb:60:in `block in invoke'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/server/worker.rb:42:in `block in work'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/callbacks.rb:117:in `block in run_callbacks'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/server/worker/active_record_connection_management.rb:16:in `block in with_database_connections'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/connection/tagged_logger_proxy.rb:24:in `block in tag'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/tagged_logging.rb:99:in `block in tagged'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/tagged_logging.rb:37:in `tagged'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/tagged_logging.rb:99:in `tagged'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/connection/tagged_logger_proxy.rb:24:in `tag'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/server/worker/active_record_connection_management.rb:16:in `with_database_connections'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/engine.rb:62:in `block (4 levels) in <class:Engine>'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/execution_wrapper.rb:88:in `wrap'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/engine.rb:57:in `block (3 levels) in <class:Engine>'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/callbacks.rb:126:in `instance_exec'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/callbacks.rb:137:in `run_callbacks'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/server/worker.rb:41:in `work'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/server/worker.rb:59:in `invoke'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/actioncable-6.1.4.1/lib/action_cable/server/worker.rb:54:in `block in async_invoke'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:363:in `run_task'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:352:in `block (3 levels) in create_worker'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:335:in `loop'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:335:in `block (2 levels) in create_worker'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:334:in `catch'
/Users/reinis/.rvm/gems/ruby-3.0.2/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:334:in `block in create_worker'To reproduce:
- Set devise timeout_in to 20 minutes
- Create ApplicationCable Connection / Channel
- Subscribe to channel
- Travel 25 minutes in future
- Error
Expected behavior
Does not throw UncaughtThrowError(uncaught throw :warden)
Temporary solution (rescue exception)
# frozen_string_literal: true
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
reject_unauthorized_connection if current_user.blank?
end
private def find_verified_user
env["warden"].user
rescue UncaughtThrowError
nil
end
end
end
Source: heartcombo/devise