devise icon indicating copy to clipboard operation
devise copied to clipboard

Use `:unprocessable_content` in generated Devise config for Rack 3.1+

Open taketo1113 opened this issue 3 months ago • 1 comments

Background

In Rack v3.1.0, the symbol for HTTP status code 422 was changed from :unprocessable_entity to :unprocessable_content. As a result, when using rack 3.2 with the following configuration in config/initializers/devise.rb, a warning is shown on login failure:

# config/initializers/devise.rb
Devise.setup do |config|
  ...
  config.responder.error_status = :unprocessable_entity

Warning message:

/path-to-app/vendor/bundle/ruby/3.4.0/gems/devise-4.9.4/lib/devise/failure_app.rb:80: warning: Status code :unprocessable_entity is deprecated and will be removed in a future version of Rack. Please use :unprocessable_content instead.

This warning can be resolved by updating the config as follows:

# config/initializers/devise.rb
Devise.setup do |config|
  ...
+  config.responder.error_status = :unprocessable_content
-  config.responder.error_status = :unprocessable_entity
  • System configuration
    • ruby: 3.4.5
    • rails: 8.0.2.1
    • devise: 4.9.4

Note that this warning continues to occur even after applying the following Rails patch:

  • https://github.com/rails/rails/pull/53383

Details

This Pull Request fixes the root cause of the warning by adjusting the generated config during $ rails generate devise:install depending on the rack version. With this change, newly generated Devise configs will no longer produce warnings. ( Fix #5791 )

# config/initializers/devise.rb
Devise.setup do |config|
  ...
+  config.responder.error_status = :unprocessable_content
-  config.responder.error_status = :unprocessable_entity

For rack 3.1 and higher, this change uses :unprocessable_content instead of :unprocessable_entity. For rack 3.0 and below, it continues to use :unprocessable_entity.

Additional information

DeviseController: Error Status

Controllers under app/controllers/devise have also been updated to return the status defined in config.responder.error_status when handling errors. If config.responder.error_status remains at its default value (:ok), the status falls back to the rack version:

  • :unprocessable_content for rack 3.1+
  • :unprocessable_entity for rack 3.0 and below
# app/controllers/devise/confirmations_controller.rb
...
       respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
     else
-      # TODO: use `error_status` when the default changes to `:unprocessable_entity`.
-      respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
+      # Use `error_status` when the default changes to `:unprocessable_content` (or `:unprocessable_entity`).
+      respond_with_navigational(resource.errors, status: (Devise.responder.error_status != :ok) ? Devise.responder.error_status : Rack::Utils::SYMBOL_TO_STATUS_CODE.key(422) ){ render :new }
     end
   end

Symbol selection between :unprocessable_entity and :unprocessable_content

The symbol selection between :unprocessable_entity and :unprocessable_content is determined by Rack:

The behavior of Rack::Utils::SYMBOL_TO_STATUS_CODE.key(422) depends on the Rack version:

Rack::Utils::SYMBOL_TO_STATUS_CODE.key(422)
=> :unprocessable_content # rack 3.1 or higher
=> :unprocessable_entity # rack 3.0 and below

The code for Rack::Utils::SYMBOL_TO_STATUS_CODE can be found here: https://github.com/rack/rack/blob/v3.2.1/lib/rack/utils.rb#L564-L566 https://github.com/rack/rack/blob/2.0.0/lib/rack/utils.rb#L581-L583

taketo1113 avatar Sep 12 '25 10:09 taketo1113

+1 works on my setup and tests pass

otherjustin avatar Oct 05 '25 11:10 otherjustin