Skip to content

Conversation

@taketo1113
Copy link
Contributor

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:

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

…ity in confirmations and unlocks controllers
For rack 3.1 and higher, devise config uses `:unprocessable_content` instead of `:unprocessable_entity`.
For rack 3.0 and below, it continues to use `:unprocessable_entity`.
@otherjustin
Copy link

+1 works on my setup and tests pass

@carlosantoniodasilva carlosantoniodasilva added this to the 5.0 milestone Oct 31, 2025
Copy link
Member

@carlosantoniodasilva carlosantoniodasilva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I think I'll have to handle this better / somehow on responders too for a more complete fix, and the controller code / conditionals seem overly verbose tbh, for anyone looking at it, so I'll need to think about it some, but I appreciate the PR! I don't have any immediate feedback other than I'll take a better look and report back when possible.

We'll continue using `:unprocessable_entity` since Rails will
transparently convert this for us for the time being.
Rack has deprecated `:unprocessable_entity` in favor of
`:unprocessable_content`, but the former has been used extensively
for years. Rails is now transparently converting that under the hood to
avoid the warnings, but our failure app wasn't going through the same
handling since it's more low level and responds to Rack directly. This
introduces the Rails translation handling if available on newer
versions, falling back to the Rack conversion which might emit the
warning if using `:unprocessable_entity` on Rack 3.1+

To fully fix it, people can configure their `error_status` to
`:unprocessable_content` on newer versions of Rack. The default for new
Devise apps will also change.

rack/rack#2137
Copy link
Member

@carlosantoniodasilva carlosantoniodasilva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taketo1113 thanks for your PR!

I updated it with a few tweaks, mainly translating the error code in the failure app and reverting the controller changes for now, and will get it merged soon.

# 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 }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's revert these controller changes and continue using :unprocessable_entity from here. I'll revisit with responders later, but for now we can rely on the fact that Rails will transparently handle it for us depending on the Rack version.

(I'm still considering making these code changes for v5, not sure it's something I want to bite just yet, as things have been working just fine for now, and there's already plenty of changes on v5 for people to upgrade... maybe in the next version)

# Note: These might become the new default in future versions of Devise.
config.responder.error_status = :unprocessable_entity
config.responder.error_status = :unprocessable_content # for Rack 3.1 or higher
# config.responder.error_status = :unprocessable_entity # for Rack 3.0 or lower

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@carlosantoniodasilva carlosantoniodasilva merged commit 8054ad5 into heartcombo:main Dec 31, 2025
62 checks passed
@taketo1113 taketo1113 deleted the update-rack-unprocessable_content branch January 2, 2026 13:10
@taketo1113
Copy link
Contributor Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Use unprocessable_content instead of unprocessable_entity for 422 status codes

4 participants