heartcombo/devise · error · NotImplementedError
"Devise doesn't know how to sanitize parameters for '#{actio
Error message
"Devise doesn't know how to sanitize parameters for '#{action}'".
If you want to define a new set of parameters to be sanitized use the
`permit` method first:
devise_parameter_sanitizer.permit(:#{action}, keys: [:param1, :param2, :param3]) What it means
Devise::ParameterSanitizer whitelists request parameters per action; defaults are pre-registered only for :sign_in, :sign_up and :account_update (DEFAULT_PERMITTED_ATTRIBUTES). sanitize(action) looks up @permitted[action] and, when nothing was registered for that action (no array and no block), calls unknown_action!, which raises NotImplementedError with instructions to call permit first. It fires whenever you sanitize an action whose permission list was never declared.
Source
Thrown at lib/devise/parameter_sanitizer.rb:163
@params[@resource_name].respond_to?(:permit)
end
def empty_params
ActionController::Parameters.new({})
end
def permit_keys(parameters, keys)
parameters.permit(*keys)
end
def extract_auth_keys(klass)
auth_keys = klass.authentication_keys
auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys
end
def unknown_action!(action)
raise NotImplementedError, <<-MESSAGE.strip_heredoc
"Devise doesn't know how to sanitize parameters for '#{action}'".
If you want to define a new set of parameters to be sanitized use the
`permit` method first:
devise_parameter_sanitizer.permit(:#{action}, keys: [:param1, :param2, :param3])
MESSAGE
end
end
end
View on GitHub (pinned to 372b295fe6)
Solutions
- Register the permission list before sanitizing, typically in a before_action: devise_parameter_sanitizer.permit(:accept_invitation, keys: [:invitation_token, :name, :password, :password_confirmation]).
- For nested/full control, use the block form: devise_parameter_sanitizer.permit(:accept_invitation) { |u| u.permit(:invitation_token, profile: [:name]) }.
- If the action is really one of the standard ones, fix the symbol: sanitize(:sign_up) not sanitize(:signup) — and make sure the permit and sanitize calls use the identical symbol.
- Ensure your before_action configure_permitted_parameters runs before the action that sanitizes (declared in ApplicationController with if: :devise_controller?).
Example fix
# before (custom Devise controller)
class Users::InvitationsController < Devise::RegistrationsController
def update
self.resource = resource_class.accept_invitation(
devise_parameter_sanitizer.sanitize(:accept_invitation) # NotImplementedError
)
end
end
# after
class Users::InvitationsController < Devise::RegistrationsController
before_action :configure_invitation_params, only: :update
def update
self.resource = resource_class.accept_invitation(
devise_parameter_sanitizer.sanitize(:accept_invitation)
)
end
protected
def configure_invitation_params
devise_parameter_sanitizer.permit(
:accept_invitation,
keys: [:invitation_token, :password, :password_confirmation]
)
end
end Defensive patterns
Strategy: validation
Validate before calling
# Register every custom action's permissions before any sanitize call:
class Users::InvitationsController < Devise::RegistrationsController
CUSTOM_SANITIZED_ACTIONS = %i(accept_invitation).freeze
before_action only: :update do
devise_parameter_sanitizer.permit(
:accept_invitation,
keys: [:invitation_token, :password, :password_confirmation]
)
end
end
# If you must check before calling sanitize (introspects the private registry):
def sanitizer_registered?(action)
devise_parameter_sanitizer
.instance_variable_get(:@permitted)
.key?(action.to_sym)
end Type guard
def sanitizable_action?(sanitizer, action) sanitizer.instance_variable_get(:@permitted).key?(action.to_sym) end # usage: params = sanitizer.sanitize(action) if sanitizable_action?(sanitizer, :accept_invitation)
Try / catch
# Around a custom sanitize call in code you don't fully control: begin attrs = devise_parameter_sanitizer.sanitize(:accept_invitation) rescue NotImplementedError devise_parameter_sanitizer.permit(:accept_invitation, keys: [:invitation_token, :password, :password_confirmation]) attrs = devise_parameter_sanitizer.sanitize(:accept_invitation) end
Prevention
- Declare permit for every custom action in a before_action (guarded by if: :devise_controller?) rather than inline in the action.
- Define action symbols once (constants or a small registry) and reuse them in both permit and sanitize calls to avoid typo drift.
- Remember only :sign_in, :sign_up, :account_update come pre-registered — any other symbol must be permitted first.
- Add a request spec per custom sanitized action so an unregistered action fails in CI, not on the first real form submission.
When it happens
Trigger: A custom Devise controller action calling devise_parameter_sanitizer.sanitize(:accept_invitation) (or any symbol outside sign_in/sign_up/account_update) without a matching devise_parameter_sanitizer.permit(:accept_invitation, keys: [...]) beforehand; a typo making the permit and sanitize symbols differ (permit(:signip) vs sanitize(:sign_in)); code migrated from the old Devise 2.x/3.x sanitizer API that had different default actions.
Common situations: Building custom registration or invitation flows on Devise controllers; overriding Devise::RegistrationsController#create and sanitizing a custom action name; large apps that whitelist extra profile fields per action and miss one; refactoring renames an action symbol in one place but not the other.
Related errors
- Could not find devise mapping for path #{request.fullpath.in
- :route should be true, a Symbol or a Hash
- #{name}
- Mapping omniauth_callbacks on a resource that is not omniaut
- 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/db9a980a04707990.
Report an issue: GitHub.