What happens is that the sign_in method pulls you out of the normal thread, throwing a boss error that will cause the application to fail.
If you look at the definition of sign_in in lib/devise/controllers/helpers.rb , you will see that in normal mode, when you first sign up for a user, you end the call
warden.set_user(resource, options.merge!(:scope => scope)
warden is a reference to the Warden::Proxy object, and if you look at what set_user does (you can see that at warden/lib/warden/proxy.rb:157-182 ), you will see that after the user is serialized in the session, makes any after_set_user .
Devise defines a group of them in lib/devise/hooks/ , and the specific one that interests us is in lib/devise/hooks/activatable.rb :
Warden::Manager.after_set_user do |record, warden, options| if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication? scope = options[:scope] warden.logout(scope) throw :warden, :scope => scope, :message => record.inactive_message end end
As you can see if the entry is not active_for_authentication? then we throw . Is this what happens in your case - active_for_authentication? returns false for a confirmable resource that has not yet been confirmed (see lib/devise/models/confirmable.rb:121-127 ).
And when we throw :warden , we call failure_app . So what happens and why you are breaking the normal control flow for your controller.
(Actually, the above is talking about the normal flow of the controller controller. I think your js block is actually redundant - calling warden.authenticate! also set up the user, so I think you quit before you even get to sign_in .)
To answer the second question, one of the possible ways to solve this problem is to create your own application with an error. By default, devset sets warden failure_app to the Devise::Delegator , which allows you to specify different applications with failures for different development models, but the default is Devise::FailureApp if nothing has been configured. You can either configure an existing application with failures, or replace it with your own application for failure by setting up a supervisor, or you can configure the delegate to use the application with a default error for html requests and delegate another application with an error for json.