ruby-concurrency/concurrent-ruby · error · ArgumentError

wrong number of arguments (#{argc} for #{arity}..*)

Error message

wrong number of arguments (#{argc} for #{arity}..*)

What it means

Same pre-dispatch check in Concurrent::Async.validate_argc, but for methods with variable arity (defined with *args or optional parameters, so Method#arity is negative). For such methods the library only enforces a minimum of (arity + 1).abs required arguments. This error fires when fewer than that minimum are passed; passing extra arguments is fine.

Source

Thrown at lib/concurrent-ruby/concurrent/async.rb:257

    # @note This check is imperfect because of the way Ruby reports the arity of
    #   methods with a variable number of arguments. It is possible to determine
    #   if too few arguments are given but impossible to determine if too many
    #   arguments are given. This check may also fail to recognize dynamic behavior
    #   of the object, such as methods simulated with `method_missing`.
    #
    # @see http://www.ruby-doc.org/core-2.1.1/Method.html#method-i-arity Method#arity
    # @see http://ruby-doc.org/core-2.1.0/Object.html#method-i-respond_to-3F Object#respond_to?
    # @see http://www.ruby-doc.org/core-2.1.0/BasicObject.html#method-i-method_missing BasicObject#method_missing
    #
    # @!visibility private
    def self.validate_argc(obj, method, *args)
      argc = args.length
      arity = obj.method(method).arity

      if arity >= 0 && argc != arity
        raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity})")
      elsif arity < 0 && (arity = (arity + 1).abs) > argc
        raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity}..*)")
      end
    end

    # @!visibility private
    def self.included(base)
      base.singleton_class.send(:alias_method, :original_new, :new)
      base.extend(ClassMethods)
      super(base)
    end

    # @!visibility private
    module ClassMethods
      def new(*args, &block)
        obj = original_new(*args, &block)
        obj.send(:init_synchronization)
        obj
      end
      ruby2_keywords :new if respond_to?(:ruby2_keywords, true)

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Pass at least `(method.arity + 1).abs` arguments — the required leading parameters of the method.
  2. When building args dynamically, check `obj.method(:m).arity`; if negative, require `(a + 1).abs` args before calling `proxy.async.m(*args)`.
  3. Give leading parameters defaults when they are truly optional, so the enforced minimum matches your intent.
  4. Rescue ArgumentError at the call site; the check is synchronous before scheduling.

Example fix

// before (def connect(host, *options))
proxy.async.connect

// after
proxy.async.connect('db.example.com', timeout: 5)
Defensive patterns

Strategy: validation

Validate before calling

m = api.method(:connect)
min_args = m.arity < 0 ? (m.arity + 1).abs : m.arity
raise ArgumentError, "connect needs >= #{min_args} args" if args.size < min_args
proxy.async.connect(*args)

Type guard

def meets_minimum_arity?(obj, meth, *args)
  a = obj.method(meth).arity
  min = a < 0 ? (a + 1).abs : a
  args.size >= min
end

Try / catch

begin
  proxy.async.connect(*args)
rescue ArgumentError => e
  raise unless e.message.include?('..*)')
  logger.error("too few args for variadic method: #{e.message}")
end

Prevention

When it happens

Trigger: `def connect(host, *options)` (arity -2) called as `proxy.async.connect`; `def run(a, b = 1)` called as `proxy.await.run`; splatting an empty array `proxy.async.setup(*[])` into a method whose leading parameter is required.

Common situations: Optional-parameter methods where callers wrongly assume every parameter is optional; dynamically built argument arrays that do not enforce the required leading parameters; wrapper methods that forward *args into methods needing at least one argument.

Related errors


AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21). Data as JSON: /api/errors/0112ec323665595e. Report an issue: GitHub.