Use `:unprocessable_content` in generated Devise config for Rack 3.1+
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_contentfor rack 3.1+:unprocessable_entityfor 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
+1 works on my setup and tests pass