padrino/padrino-framework · error · Padrino::Admin::AccessControlError

Role #{role} must be present and must be a symbol!

Error message

Role #{role} must be present and must be a symbol!

What it means

Padrino::Admin::AccessControl::Base#roles_for maps project modules to roles and validates its arguments: at least one role must be supplied and every role must be a Symbol (:admin, not 'admin'). Note a latent bug in this source: the message interpolates an undefined variable `role` (the parameter is `roles`), so when this branch trips you may actually see a NameError about `role` instead of this text — the root cause is still an empty or non-Symbol role list.

Source

Thrown at padrino-admin/lib/padrino-admin/access_control.rb:47

          app.send(:access_control=, Padrino::Admin::AccessControl::Base.new)
        end
        alias included registered
      end

      ##
      # This base access control class where roles are defined as are authorizations.
      #
      class Base
        def initialize
          @roles, @authorizations, @project_modules = [], [], []
        end

        ##
        # We map project modules for a given role or roles.
        #
        def roles_for(*roles, &block)
          raise Padrino::Admin::AccessControlError, "Role #{role} must be present and must be a symbol!" if roles.any? { |r| !r.is_a?(Symbol) } || roles.empty?
          raise Padrino::Admin::AccessControlError, "You can't merge :any with other roles" if roles.size > 1 && roles.any? { |r| r == :any }

          @roles += roles
          @authorizations << Authorization.new(*roles, &block)
        end

        ##
        # Return an array of roles.
        #
        def roles
          @roles.uniq.reject { |r| r == :any }
        end

        ##
        # Return an array of project_modules.
        #
        def project_modules(account)
          role = account.role.to_sym rescue :any

View on GitHub (pinned to 167044f3d5)

Solutions

  1. Pass at least one role and use Symbols: access_control.roles_for(:admin) do |role| ... end
  2. When roles come from config or the database, convert first: roles_for(*roles.map(&:to_sym))
  3. If you instead saw a NameError mentioning `role`, you hit the interpolation bug at access_control.rb:47 — fix the call as above, or patch the message to interpolate roles.inspect

Example fix

# before
access_control.roles_for('admin') do |role| ... end

# after
access_control.roles_for(:admin) do |role| ... end

# dynamic roles
access_control.roles_for(*config_roles.map(&:to_sym)) do |role| ... end
Defensive patterns

Strategy: validation

Validate before calling

roles = Array(config_roles)
if roles.empty? || roles.any? { |r| !r.is_a?(Symbol) }
  raise ArgumentError, "roles must be non-empty Symbols: #{roles.inspect}"
end
access_control.roles_for(*roles) { |role| ... }

Type guard

def valid_roles?(*roles) = !roles.empty? && roles.all? { |r| r.is_a?(Symbol) }

Try / catch

begin
  access_control.roles_for(*roles, &block)
rescue Padrino::Admin::AccessControlError, NameError
  warn "invalid access_control roles: #{roles.inspect}" # boot-time config bug — fix and re-run
  raise
end

Prevention

When it happens

Trigger: Calling access_control.roles_for with no arguments (roles.empty?); passing strings like roles_for('admin'); passing role values read from ENV, YAML, params or a database that arrive as Strings; splatting a dynamic array whose entries are not Symbols.

Common situations: Editing the access_control block in admin/app.rb and quoting the role; building role lists from config files or a roles table; copying examples where the role came from user input (always a String).

Related errors


AI-assisted analysis of padrino/padrino-framework@167044f3d5 (2026-08-23). Data as JSON: /api/errors/82f845c7de799aaf. Report an issue: GitHub.