heartcombo/devise · error · AbstractController::ActionNotFound
Could not find devise mapping for path #{request.fullpath.in
Error message
Could not find devise mapping for path #{request.fullpath.inspect}.
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller"
end
2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:
@request.env["devise.mapping"] = Devise.mappings[:user]
What it means
Every Devise controller derives its resource scope (User, Admin, etc.) from request.env["devise.mapping"], which is set by the routing scope that devise_for/devise_scope installs. DeviseController runs assert_is_devise_resource! as a prepend_before_action on every request, and when the mapping env entry is nil it raises AbstractController::ActionNotFound with this message. In practice it means a Devise controller action was reached outside of any Devise-mapped route, or a controller test hit the controller directly without bypassing the router being compensated for.
Source
Thrown at app/controllers/devise_controller.rb:104
get "/some/route" => "some_devise_controller"
end
2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:
@request.env["devise.mapping"] = Devise.mappings[:user]
MESSAGE
end
# Returns real navigational formats which are supported by Rails
def navigational_formats
@navigational_formats ||= Devise.navigational_formats.select { |format| Mime::EXTENSION_LOOKUP[format.to_s] }
end
def unknown_action!(msg)
logger.debug "[Devise] #{msg}" if logger
raise AbstractController::ActionNotFound, msg
end
# Sets the resource creating an instance variable
def resource=(new_resource)
instance_variable_set(:"@#{resource_name}", new_resource)
end
# Helper for use in before_actions where no authentication is required.
#
# Example:
# before_action :require_no_authentication, only: :new
def require_no_authentication
assert_is_devise_resource!
return unless is_navigational_format?
no_input = devise_mapping.no_input_strategies
authenticated = if no_input.present?
args = no_input.dup.push scope: resource_nameView on GitHub (pinned to 372b295fe6)
Solutions
- Wrap the offending route in a devise_scope block: devise_scope :user do get "/users/invite" => "devise/registrations#invite" end (use the scope name from devise_for).
- If this happens in a controller/request spec, set the mapping explicitly in setup/before: @request.env["devise.mapping"] = Devise.mappings[:user].
- If the route is genuinely custom logic, point it at your own controller that inherits from the Devise controller only when it is also mounted inside devise_scope; otherwise inherit from ApplicationController.
- Prefer request specs (rails generate rspec:request or Rails' integration tests) that go through the real router, which sets the mapping automatically.
Example fix
# before (config/routes.rb)
get "/users/invite" => "devise/registrations#invite"
# after
scope "/users" do
devise_scope :user do
get "/invite" => "devise/registrations#invite", as: :invite
end
end
# spec fix (controller specs bypassing the router)
# before
RSpec.describe Devise::RegistrationsController, type: :controller do
describe "GET #new" do
it "renders" do
get :new
end
end
end
# after
RSpec.describe Devise::RegistrationsController, type: :controller do
before do
@request.env["devise.mapping"] = Devise.mappings[:user]
end
describe "GET #new" do
it "renders" do
get :new
expect(response).to have_http_status(:ok)
end
end
end Defensive patterns
Strategy: validation
Validate before calling
# In a custom Devise controller, verify the mapping before relying on it:
class Devise::RegistrationsController
protected
def devise_mapping_present?
request.env["devise.mapping"].present?
end
end
# In controller specs, always install the mapping in before/setup so the
# prepend_before_action assert_is_devise_resource! never fires:
# @request.env["devise.mapping"] = Devise.mappings[:user] Type guard
# Predicate usable as a before_action guard for custom Devise routes:
def devise_scope_mapped?(request)
!request.env["devise.mapping"].nil?
end
# usage: before_action -> { head :not_found unless devise_scope_mapped?(request) } Prevention
- Always mount custom routes that target Devise controllers inside devise_scope <resource> in config/routes.rb.
- In controller specs for Devise controllers, set @request.env["devise.mapping"] = Devise.mappings[:user] in a before block — or prefer request specs that exercise the real router.
- After any routes.rb refactor, hit one Devise route per scope in a smoke test so a missing mapping surfaces in CI, not production.
- Use the devise_mapping view/controller helper to inspect which scope a request resolved to when debugging.
When it happens
Trigger: 1) Routing a custom endpoint straight to a Devise controller, e.g. get "/users/invite" => "devise/registrations#invite", outside a devise_scope :user block, so the mapping middleware never runs. 2) Request/controller specs on a Devise controller that call get :new, params: {} directly (bypassing the router) without first setting @request.env["devise.mapping"] = Devise.mappings[:user]. 3) Reusing a Devise controller subclass for a non-Devise route that was never wrapped in devise_scope.
Common situations: Adding custom actions to Devise controllers (invite flows, profile pages) and mounting them with plain routes; writing controller tests for Devise or inherited Devise controllers instead of request/integration specs; refactoring routes.rb and accidentally moving a Devise route out of its scope; namespaced engines routing to Devise controllers without re-declaring the scope.
Related errors
- #{name}
- Mapping omniauth_callbacks on a resource that is not omniaut
- :route should be true, a Symbol or a Hash
- "Devise doesn't know how to sanitize parameters for '#{actio
- An ORM must be set to install Devise in your application. B
AI-assisted analysis of heartcombo/devise@372b295fe6 (2026-08-21).
Data as JSON: /api/errors/5cb3c0804cb51de5.
Report an issue: GitHub.