{"record":{"id":"85ad3db93bb5322a","repo":"ruby-concurrency/concurrent-ruby","slug":"cannot-use-both-value-and-block-as-default-value","errorCode":null,"errorMessage":"Cannot use both value and block as default value","messagePattern":"Cannot use both value and block as default value","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb","lineNumber":51,"sourceCode":"  #\n  #   Fiber.new do\n  #     v.value #=> 14\n  #     v.value = 2\n  #     v.value #=> 2\n  #   end.resume\n  #\n  #   v.value #=> 14\n  class FiberLocalVar\n    LOCALS = FiberLocals.new\n\n    # Creates a fiber local variable.\n    #\n    # @param [Object] default the default value when otherwise unset\n    # @param [Proc] default_block Optional block that gets called to obtain the\n    #   default value for each fiber\n    def initialize(default = nil, &default_block)\n      if default && block_given?\n        raise ArgumentError, \"Cannot use both value and block as default value\"\n      end\n\n      if block_given?\n        @default_block = default_block\n        @default = nil\n      else\n        @default_block = nil\n        @default = default\n      end\n\n      @index = LOCALS.next_index(self)\n    end\n\n    # Returns the value in the current fiber's copy of this fiber-local variable.\n    #\n    # @return [Object] the current value\n    def value\n      LOCALS.fetch(@index) { default }","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb#L33-L69","documentation":"`Concurrent::FiberLocalVar.new(default = nil, &default_block)` accepts exactly one default mechanism: a static value or a block evaluated per fiber on first read there. The guard is `default && block_given?`, so a truthy default plus a block raises ArgumentError, while `nil` (or `false`) plus a block is silently allowed and the block wins. The conflict is rejected because the resolution order between a static value and a computed block would be ambiguous.","triggerScenarios":"`Concurrent::FiberLocalVar.new('n/a') { Fiber.current.object_id }` — truthy string plus block. Configuration DSLs that always capture a block while also passing a fallback positional. Wrappers forwarding both `default` and `&blk` from their own signature.","commonSituations":"A config object that gained a per-fiber block default later while keeping the old value argument; copy-paste between ThreadLocalVar and FiberLocalVar where both were being 'combined'; helpers that accept `value = nil, &blk` and forward both unconditionally.","solutions":["Pick one mechanism: `FiberLocalVar.new { per_fiber_default }` for computed defaults or `FiberLocalVar.new(value)` for constants.","In wrappers, forward only the winner: `blk ? FiberLocalVar.new(&blk) : FiberLocalVar.new(value)`.","Remember the asymmetry: only truthy values conflict — `nil`/`false` with a block quietly uses the block, which can mask intent."],"exampleFix":"// before\nreq_id = Concurrent::FiberLocalVar.new('none') { Fiber.current.object_id.to_s(36) }\n\n// after\nreq_id = Concurrent::FiberLocalVar.new { Fiber.current.object_id.to_s(36) }","handlingStrategy":"validation","validationCode":"def fiber_local(default = nil, &blk)\n  raise ArgumentError, 'default value or block, not both' if default && blk\n  Concurrent::FiberLocalVar.new(default, &blk)\nend","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Choose a static default or a per-fiber block, never both.","Note nil/false plus a block is silently allowed (block wins) — be explicit anyway.","Wrap construction in helpers that enforce the either/or rule."],"tags":["concurrent-ruby","fiberlocalvar","argumenterror","default-value"],"backgroundTag":"mutually-exclusive-arguments","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}