{"record":{"id":"dfd54325f4526a7a","repo":"ruby-concurrency/concurrent-ruby","slug":"must-set-with-either-a-value-or-a-block","errorCode":null,"errorMessage":"must set with either a value or a block","messagePattern":"must set with either a value or a block","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/ivar.rb","lineNumber":204,"sourceCode":"      self\n    end\n\n    # @!visibility private\n    def notify_observers(value, reason)\n      observers.notify_and_delete_observers{ [Time.now, value, reason] }\n    end\n\n    # @!visibility private\n    def ns_complete_without_notification(success, value, reason)\n      raise MultipleAssignmentError if [:fulfilled, :rejected].include? @state\n      set_state(success, value, reason)\n      event.set\n    end\n\n    # @!visibility private\n    def check_for_block_or_value!(block_given, value) # :nodoc:\n      if (block_given && value != NULL) || (! block_given && value == NULL)\n        raise ArgumentError.new('must set with either a value or a block')\n      end\n    end\n  end\nend\n","sourceCodeStart":186,"sourceCodeEnd":209,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/ivar.rb#L186-L209","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nivar.set\n# or\nivar.set(1) { compute }\n\n# after\nivar.set(1)\n# or\nivar.set { compute }","handlingStrategy":"validation","validationCode":"def complete_ivar(ivar, value = :__unset__, &block)\n  has_value = (value != :__unset__)\n  raise ArgumentError, 'set with exactly one of value or block' if has_value == !!block\n  has_value ? ivar.set(value) : ivar.set(&block)\nend","typeGuard":null,"tryCatchPattern":"begin\n  ivar.set(value)\nrescue ArgumentError => e\n  raise ArgumentError, 'set needs exactly a value or a block' if /value or a block/.match?(e.message)\n  raise\nrescue Concurrent::MultipleAssignmentError\n  logger.warn('ivar already completed; ignoring duplicate set')\nend","preventionTips":["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"],"tags":["ruby","concurrency","ivar","write-once","argument-validation"],"backgroundTag":"mutually-exclusive-arguments","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}