ruby-concurrency/concurrent-ruby · error · ArgumentError
should pass observer as a first argument or block
Error message
should pass observer as a first argument or block
What it means
`CopyOnNotifyObserverSet#add_observer(observer = nil, func = :update, &block)` requires either an observer object (on which `func`, default `:update`, will be called) or a block (invoked via `:call`). Called with neither — bare `add_observer` or `add_observer(nil)` — it raises ArgumentError before touching the set. This set is one of the two observer strategies behind Concurrent::Observable; the sibling error 'cannot provide both an observer and a block' guards the opposite misuse.
Source
Thrown at lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb:22
module Collection
# A thread safe observer set implemented using copy-on-read approach:
# observers are added and removed from a thread safe collection; every time
# a notification is required the internal data structure is copied to
# prevent concurrency issues
#
# @api private
class CopyOnNotifyObserverSet < Synchronization::LockableObject
def initialize
super()
synchronize { ns_initialize }
end
# @!macro observable_add_observer
def add_observer(observer = nil, func = :update, &block)
if observer.nil? && block.nil?
raise ArgumentError, 'should pass observer as a first argument or block'
elsif observer && block
raise ArgumentError.new('cannot provide both an observer and a block')
end
if block
observer = block
func = :call
end
synchronize do
@observers[observer] = func
observer
end
end
# @!macro observable_delete_observer
def delete_observer(observer)
synchronize doView on GitHub (pinned to 0b88d5ff75)
Solutions
- Pass the observer object: `add_observer(MyObserver.new)`.
- Or pass a block: `add_observer { |*args| handle(*args) }`.
- Guard optionality at the call site: `add_observer(obs) if obs`, or default the observer (`obs ||= DefaultObserver.new`).
- In wrapper APIs, raise your own descriptive error when both observer and block are missing.
Example fix
// before
add_observer(@listener) # @listener is nil from missing config
// after
add_observer(@listener) if @listener
# or always attach a handler:
add_observer { |*args| @listener&.call(*args) } Defensive patterns
Strategy: validation
Validate before calling
def attach(set, observer = nil, func = :update, &block)
return set.add_observer(observer, func, &block) if observer || block
raise ArgumentError, 'observer object or block required'
end
# usage
attach(set, obs) if obs
attach(set) { |*a| handle(*a) } if obs.nil? Type guard
def observer_supplied?(observer = nil, &block) !observer.nil? || !block.nil? end
Try / catch
begin
set.add_observer(obs)
rescue ArgumentError => e
raise unless e.message.include?('observer as a first argument')
set.add_observer { |*args| fallback.call(*args) }
end Prevention
- Treat the observer as required: default it (obs ||= NOOP_OBSERVER) before calling.
- Never forward possibly-nil callback variables into add_observer.
- Remember the mirrored rule: observer AND block together is also rejected.
When it happens
Trigger: `observable.add_observer` with no arguments; `add_observer(nil)`; forwarding a variable that was never assigned: `add_observer(@observer)` when config omitted the key. `add_observer(nil, &blk)` is fine (block path).
Common situations: Optional-callback APIs where the observer comes from config or params and can be absent; builder DSLs that defer attaching and accidentally attach nil; refactor that moved observer construction after the attach call.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- should pass observer as a first argument or block
- cannot provide both an observer and a block
- cannot give arguments and a block
- cannot provide both an observer and a block
- cannot give arguments and a block
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/08c3a086a813ceb0.
Report an issue: GitHub.