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

You can't merge :any with other roles

Error message

You can't merge :any with other roles

What it means

roles_for refuses to combine the special role :any with concrete roles. :any already matches every account, so merging it with :admin would make it ambiguous which authorization block governs a request. Give :any its own single-role block, or list only concrete roles.

Source

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

          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
          authorizations = @authorizations.find_all { |auth| auth.roles.include?(role) }

View on GitHub (pinned to 167044f3d5)

Solutions

  1. Split into two blocks: roles_for(:any) { ... } and roles_for(:admin) { ... }
  2. If :any was unintended, remove it and keep only the concrete roles
  3. When building role lists dynamically, reject :any first (roles.reject { |r| r == :any }) or assert it is the sole entry before calling roles_for

Example fix

# before
access_control.roles_for(:any, :admin) do |role| ... end

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

Strategy: validation

Validate before calling

roles = roles.map(&:to_sym)
roles = roles.reject { |r| r == :any } unless roles == [:any]
access_control.roles_for(*roles) { |role| ... }

Type guard

def any_merge_safe?(*roles) = roles.size == 1 || !roles.include?(:any)

Prevention

When it happens

Trigger: roles_for(:any, :admin); splatting an array that contains :any together with other roles (e.g. [:any] + stored_roles); refactoring a role list and leaving a stray :any in place.

Common situations: Trying to express 'admins plus everyone else'; iterating stored role records where one entry is 'any'; copy-pasting an existing roles_for block and appending :any.

Related errors


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