ruby-concurrency/concurrent-ruby · error · ArgumentError

cannot give arguments and a block

Error message

cannot give arguments and a block

What it means

notify_to delivers a notification to a set of registered observers in CopyOnNotifyObserverSet. It accepts either a fixed argument list (the same args delivered to every observer) or a block (args recomputed per observer via yield), never both — mixing them would leave the delivered arguments ambiguous, so it raises ArgumentError. This is an extension point, typically hit by custom notification strategies or direct calls to the observer set.

Source

Thrown at lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb:99

        @observers = {}
      end

      private

      def duplicate_and_clear_observers
        synchronize do
          observers = @observers.dup
          @observers.clear
          observers
        end
      end

      def duplicate_observers
        synchronize { @observers.dup }
      end

      def notify_to(observers, *args)
        raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
        observers.each do |observer, function|
          args = yield if block_given?
          observer.send(function, *args)
        end
      end
    end
  end
end

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Pass either the args or the block: `notify_to(observers, :changed, 42)` or `notify_to(observers) { per_observer_args }`.
  2. In forwarding wrappers, branch explicitly: `block ? notify_to(obs, &block) : notify_to(obs, *args)`.

Example fix

// before
set.notify_to(observers, :changed, payload) { fresh_payload }

// after
set.notify_to(observers, :changed, payload)
// or
set.notify_to(observers) { fresh_payload }
Defensive patterns

Strategy: validation

Validate before calling

if block_given?
  set.notify_to(observers) { yield }
else
  set.notify_to(observers, *args)
end

Try / catch

begin
  set.notify_to(observers, :changed, payload) { fresh }
rescue ArgumentError => e
  raise unless e.message == 'cannot give arguments and a block'
  set.notify_to(observers, :changed, payload)
end

Prevention

When it happens

Trigger: `observer_set.notify_to(observers, :changed, 42) { compute_args }`; custom Observable subclasses whose notify wrappers forward both *args and a block into notify_to.

Common situations: Decorator or middleware layers that blindly forward *args and █ copying notification code from another set implementation that also passes positional args.

Related errors


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