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

  1. Register the permission list before sanitizing, typically in a before_action: devise_parameter_sanitizer.permit(:accept_invitation, keys: [:invitation_token, :name, :password, :password_confirmation]).
  2. For nested/full control, use the block form: devise_parameter_sanitizer.permit(:accept_invitation) { |u| u.permit(:invitation_token, profile: [:name]) }.
  3. 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.
  4. 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

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


AI-assisted analysis of heartcombo/devise@372b295fe6 (2026-08-21). Data as JSON: /api/errors/db9a980a04707990. Report an issue: GitHub.