ruby-grape/grape · error · ArgumentError

except_values Proc must have arity of zero (use values: with

Error message

except_values Proc must have arity of zero (use values: with a one-arity predicate for per-element checks)

What it means

`except_values:` accepts a static list or a Proc. The Proc form is evaluated per request with no arguments (so fresh data such as a DB query is used), which means it must have arity zero. A Proc with parameters would be called with none and return garbage, so the validator raises ArgumentError at definition time. Per-element predicates (arity one) belong under `values:`, which the message explicitly points you to.

Source

Thrown at lib/grape/validations/validators/except_values_validator.rb:13

# frozen_string_literal: true

module Grape
  module Validations
    module Validators
      class ExceptValuesValidator < Base
        default_message_key :except_values

        def initialize(attrs, options, required, scope, opts)
          super
          except = option_value
          except_proc = except.is_a?(Proc)
          raise ArgumentError, 'except_values Proc must have arity of zero (use values: with a one-arity predicate for per-element checks)' if except_proc && !except.arity.zero?

          # Zero-arity procs (e.g. -> { User.pluck(:role) }) must be called per-request,
          # not at definition time, so they are wrapped in a lambda to defer execution.
          @excepts_call = except_proc ? except : -> { except }
        end

        def validate_param!(attr_name, params)
          return unless hash_like?(params) && params.key?(attr_name)

          excepts = @excepts_call.call
          return if excepts.nil?

          param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
          return if param_array.none? { |param| excepts.include?(param) }

          validation_error!(attr_name)
        end
      end

View on GitHub (pinned to 22d7975629)

Solutions

  1. Use a zero-arity Proc that RETURNS the excluded list: `except_values: -> { Role.blocked.pluck(:name) }`
  2. For per-element allow checks, use `values:` with a one-arity predicate: `values: ->(v) { ALLOWED.include?(v) }` (truthy = allowed)
  3. Use a plain array when the excluded set is static: `except_values: %w[admin root]`

Example fix

# before
except_values: ->(role) { role != 'admin' }

# after — per-request excluded list (zero-arity)
except_values: -> { Role.blocked.pluck(:name) }

# after — per-element allow check (belongs under values:)
values: ->(v) { ALLOWED_ROLES.include?(v) }
Defensive patterns

Strategy: type-guard

Validate before calling

def except_values_proc_ok?(opt)
  !opt.is_a?(Proc) || opt.arity.zero?
end

raise ArgumentError, 'except_values proc must return the list (zero arity)' unless except_values_proc_ok?(xopts[:except_values])

Type guard

def zero_arity_proc?(opt)
  !opt.is_a?(Proc) || opt.arity.zero?
end

# per-element predicates belong under values:, where arity one is required:
def one_arity_predicate?(opt)
  !opt.is_a?(Proc) || opt.arity == 1
end

Try / catch

begin
  optional :role, type: String, except_values: except_proc
rescue ArgumentError => e
  raise "use values: with a one-arity predicate for per-element checks: #{e.message}"
end

Prevention

When it happens

Trigger: `except_values: ->(role) { role != 'admin' }` (one-arity predicate); `except_values: method(:blocked_roles)` where the method expects arguments; `except_values: proc { |x| !x.blank? }`.

Common situations: Copying a `values:`-style predicate into `except_values:`; reusing a validation proc from another library that expects the element as its argument.

Related errors


AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21). Data as JSON: /api/errors/e5b4cf0720b06d66. Report an issue: GitHub.