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

Concurrent::Async gives any object async/await proxies that schedule method calls on a background thread. A native Ruby arity failure would be raised inside that delegated execution where the caller cannot see it, so Async.validate_argc re-checks the argument count against Method#arity synchronously, before the call is scheduled. This variant fires when the target method has a fixed arity and the number of arguments passed differs from it.

Source

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

    # @raise [ArgumentError] the given `args` do not match the arity of `method`
    #
    # @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

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Match the argument count at the call site to the method definition (count the required parameters).
  2. Before dispatching dynamic args, check `obj.method(:foo).arity` and only call `proxy.async.foo(*args)` when the counts agree.
  3. If the target legitimately accepts variable input, give the method an `*args` parameter so its arity is negative and only a minimum is enforced.
  4. Wrap the async call in begin/rescue ArgumentError — validate_argc runs on the calling thread, so the mismatch is catchable at the call site.

Example fix

// before (def transfer(from, to, amount))
client.async.transfer(from_acct, to_acct)

// after
client.async.transfer(from_acct, to_acct, 125_00)
Defensive patterns

Strategy: validation

Validate before calling

m = payment_gateway.method(:transfer)
args = [from, to, amount]
valid = m.arity >= 0 ? m.arity == args.size : args.size >= (m.arity + 1).abs
proxy.async.transfer(*args) if valid

Type guard

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

Try / catch

begin
  proxy.async.transfer(from, to)
rescue ArgumentError => e
  raise unless e.message.start_with?('wrong number of arguments')
  logger.error("arity mismatch before dispatch: #{e.message}")
end

Prevention

When it happens

Trigger: Calling a delegated method with the wrong count of arguments: `string = Concurrent::Async.wrap('ab')` then `string.async.upcase(1)` (upcase takes 0 args); `obj.await.foo(1)` when the method is `def foo(a, b)`; splatting a dynamically built array `obj.async.foo(*args)` where args.size does not equal the method's fixed arity.

Common situations: Refactoring a method signature (adding or removing a parameter) without updating async/await call sites; forwarding *args collected elsewhere that have the wrong length; test stubs whose arity differs from the real object; Ruby 2-to-3 keyword-argument changes shifting effective arities.

Related errors


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