Environment
- Devise: 5.0.2
- Rails: 8.1.2
- Ruby: 4.0.1
- Warden: 1.2.9
eager_load: false (development default)
Summary
After restarting both the Rails server and the browser, the first request to the app results in current_user: nil even though a valid remember_user_token cookie exists. Reloading the page immediately restores the authenticated state. This happens only in development (eager_load=false). Setting eager_load=true or expire_after on the session store prevents the issue.
Steps to Reproduce
- Sign in with
remember_me: true
- Restart the Rails server
- Restart the browser (clears session cookie, but remember_me cookie persists)
- Open the site → renders as logged out (
current_user: nil)
- Reload the page → logged in correctly
Debug Findings
Added the following to ApplicationController:
before_action :debug_auth
def debug_auth
Rails.logger.debug "remember cookie: #{cookies['remember_user_token'].present?}"
Rails.logger.debug "warden session: #{session['warden.user.user.key'].inspect}"
Rails.logger.debug "current_user: #{current_user.inspect}"
Rails.logger.debug "winning_strategy: #{request.env['warden'].winning_strategy.inspect}"
end
First request (after server + browser restart):
remember cookie: true
warden session: nil
current_user: nil
winning_strategy: nil
Second request (reload):
remember cookie: true
warden session: nil # still nil — recovery is via rememberable
current_user: #<User id: 1 ...>
winning_strategy: #<Devise::Strategies::Rememberable ...>
Key observations:
Warden::Strategies._strategies.key?(:rememberable) → true (globally registered)
winning_strategy is nil on the first request, meaning :rememberable is not being tried by Warden for the :user scope on the first request
- Explicitly calling
warden.authenticate(:rememberable, scope: :user) in a before_action resolves the issue entirely
Root Cause Hypothesis
In development, eager_load=false means the User model is not loaded at boot time. The User model's devise :rememberable declaration is what triggers registration of :rememberable into Warden's default_strategies for the :user scope.
In Devise v5, initialization-time autoloading was removed (to comply with Zeitwerk's prohibition on autoloading during initialization). As a result, on the first request after a server restart, User has not yet been loaded → :rememberable is not in default_strategies[:user] → Warden does not attempt the strategy → current_user returns nil.
By the second request the constant has been autoloaded (as a side effect of the first request's processing), so :rememberable is registered and authentication works.
This did not reproduce with eager_load=true, confirming the autoloading timing as the root cause.
Workaround
# app/controllers/application_controller.rb
before_action :restore_remember_me_session
private
def restore_remember_me_session
return if warden.authenticated?(:user)
return if cookies["remember_user_token"].blank?
warden.authenticate(:rememberable, scope: :user)
end
Expected Behavior
:rememberable should be attempted on the first request regardless of whether eager_load is enabled, as long as the remember_user_token cookie is present.
Notes
- This is easy to miss: it only occurs when the browser and server are restarted simultaneously (e.g. start of a work day). A simple page reload recovers the session, so many users never notice or attribute it to a one-off glitch.
expire_after on the session store also masks the issue because the session cookie survives browser restart, bypassing the rememberable path entirely.
Environment
eager_load:false(development default)Summary
After restarting both the Rails server and the browser, the first request to the app results in
current_user: nileven though a validremember_user_tokencookie exists. Reloading the page immediately restores the authenticated state. This happens only in development (eager_load=false). Settingeager_load=trueorexpire_afteron the session store prevents the issue.Steps to Reproduce
remember_me: truecurrent_user: nil)Debug Findings
Added the following to
ApplicationController:First request (after server + browser restart):
Second request (reload):
Key observations:
Warden::Strategies._strategies.key?(:rememberable)→true(globally registered)winning_strategyisnilon the first request, meaning:rememberableis not being tried by Warden for the:userscope on the first requestwarden.authenticate(:rememberable, scope: :user)in abefore_actionresolves the issue entirelyRoot Cause Hypothesis
In development,
eager_load=falsemeans theUsermodel is not loaded at boot time. TheUsermodel'sdevise :rememberabledeclaration is what triggers registration of:rememberableinto Warden'sdefault_strategiesfor the:userscope.In Devise v5, initialization-time autoloading was removed (to comply with Zeitwerk's prohibition on autoloading during initialization). As a result, on the first request after a server restart,
Userhas not yet been loaded →:rememberableis not indefault_strategies[:user]→ Warden does not attempt the strategy →current_userreturnsnil.By the second request the constant has been autoloaded (as a side effect of the first request's processing), so
:rememberableis registered and authentication works.This did not reproduce with
eager_load=true, confirming the autoloading timing as the root cause.Workaround
Expected Behavior
:rememberableshould be attempted on the first request regardless of whethereager_loadis enabled, as long as theremember_user_tokencookie is present.Notes
expire_afteron the session store also masks the issue because the session cookie survives browser restart, bypassing the rememberable path entirely.