ruby-concurrency/concurrent-ruby · error · ArgumentError
must set with either a value or a block
Error message
must set with either a value or a block
What it means
IVar#set (and the shared check_for_block_or_value! it calls, also used by Future#set and Promise#set) completes the IVar with either a value or a block — exactly one. It raises ArgumentError 'must set with either a value or a block' when you pass neither (bare ivar.set) or both (ivar.set(1) { ... }). Note that set(nil) is legal: nil is a real value, distinct from the internal NULL sentinel default.
Source
Thrown at lib/concurrent-ruby/concurrent/ivar.rb:204
self
end
# @!visibility private
def notify_observers(value, reason)
observers.notify_and_delete_observers{ [Time.now, value, reason] }
end
# @!visibility private
def ns_complete_without_notification(success, value, reason)
raise MultipleAssignmentError if [:fulfilled, :rejected].include? @state
set_state(success, value, reason)
event.set
end
# @!visibility private
def check_for_block_or_value!(block_given, value) # :nodoc:
if (block_given && value != NULL) || (! block_given && value == NULL)
raise ArgumentError.new('must set with either a value or a block')
end
end
end
end
View on GitHub (pinned to 0b88d5ff75)
Solutions
- Pass exactly one: ivar.set(value) or ivar.set { compute }
- To complete with 'no result', pass an explicit nil: ivar.set(nil)
- Do not call set to reset or signal — IVars are write-once; create a new IVar instead
Example fix
# before
ivar.set
# or
ivar.set(1) { compute }
# after
ivar.set(1)
# or
ivar.set { compute } Defensive patterns
Strategy: validation
Validate before calling
def complete_ivar(ivar, value = :__unset__, &block) has_value = (value != :__unset__) raise ArgumentError, 'set with exactly one of value or block' if has_value == !!block has_value ? ivar.set(value) : ivar.set(&block) end
Try / catch
begin
ivar.set(value)
rescue ArgumentError => e
raise ArgumentError, 'set needs exactly a value or a block' if /value or a block/.match?(e.message)
raise
rescue Concurrent::MultipleAssignmentError
logger.warn('ivar already completed; ignoring duplicate set')
end Prevention
- Remember IVars are write-once: set(nil) is valid, bare set() is not
- Wrap completion in one helper that enforces the exactly-one rule
- Distinguish this ArgumentError from MultipleAssignmentError in rescue clauses — they mean different bugs
When it happens
Trigger: Calling ivar.set with no arguments (often an attempt to 'flush' or 'reset' the IVar); passing both a value and a block; passing the value inside the block's closure and calling set() bare by mistake.
Common situations: Treating set like a no-arg completion signal; refactoring from value-style to block-style completion and leaving a bare call; confusion with #complete/#fulfill-style APIs of other promise libraries.
Related errors
- provide only a value or a block
- cannot provide both an observer and a block
- Concurrent::MultipleAssignmentError
- `min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}
- `min_threads` cannot be more than `max_threads`
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/dfd54325f4526a7a.
Report an issue: GitHub.