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)
objView on GitHub (pinned to 0b88d5ff75)
Solutions
- Match the argument count at the call site to the method definition (count the required parameters).
- Before dispatching dynamic args, check `obj.method(:foo).arity` and only call `proxy.async.foo(*args)` when the counts agree.
- If the target legitimately accepts variable input, give the method an `*args` parameter so its arity is negative and only a minimum is enforced.
- 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
- Re-run the test suite against async/await call sites whenever a method signature changes.
- Never splat unvalidated arrays into async calls; check the length against Method#arity first.
- Remember the check runs on the caller thread, so unlike errors inside the delegated call, this ArgumentError can be rescued locally.
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
- wrong number of arguments (#{argc} for #{arity}..*)
- unbuffered channels cannot have a capacity
- capacity must be at least 1 for this buffer type
- no block given
- size must be greater than 0
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/165594be97490927.
Report an issue: GitHub.