{"record":{"id":"fa4bc77df02944d2","repo":"ruby-concurrency/concurrent-ruby","slug":"capacity-must-be-at-least-1-for-this-buffer-type","errorCode":null,"errorMessage":"capacity must be at least 1 for this buffer type","messagePattern":"capacity must be at least 1 for this buffer type","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby-edge/concurrent/channel.rb","lineNumber":66,"sourceCode":"      # undocumented -- for internal use only\n      if opts.is_a? Buffer::Base\n        self.buffer = opts\n        return\n      end\n\n      capacity = opts[:capacity] || opts[:size]\n      buffer = opts[:buffer]\n\n      if capacity && buffer == :unbuffered\n        raise ArgumentError.new('unbuffered channels cannot have a capacity')\n      elsif capacity.nil? && buffer.nil?\n        self.buffer = BUFFER_TYPES[:unbuffered].new\n      elsif capacity == 0 && buffer == :buffered\n        self.buffer = BUFFER_TYPES[:unbuffered].new\n      elsif buffer == :unbuffered\n        self.buffer = BUFFER_TYPES[:unbuffered].new\n      elsif capacity.nil? || capacity < 1\n        raise ArgumentError.new('capacity must be at least 1 for this buffer type')\n      else\n        buffer ||= :buffered\n        self.buffer = BUFFER_TYPES[buffer].new(capacity)\n      end\n\n      self.validator = opts.fetch(:validator, DEFAULT_VALIDATOR)\n    end\n\n    def put(item)\n      return false unless validate(item, false, false)\n      do_put(item)\n    end\n    alias_method :send, :put\n    alias_method :<<, :put\n\n    def put!(item)\n      validate(item, false, true)\n      ok = do_put(item)","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby-edge/concurrent/channel.rb#L48-L84","documentation":"Channel#initialize validates capacity for the buffered buffer types (:buffered, :dropping, :sliding). After special cases are handled (no options -> unbuffered; capacity == 0 with buffer: :buffered -> unbuffered; buffer: :unbuffered), any remaining nil or sub-1 capacity raises, because a buffered/dropping/sliding buffer needs at least one slot.","triggerScenarios":"Concurrent::Channel.new(buffer: :dropping) with no capacity; Concurrent::Channel.new(capacity: 0) with no :buffer key (0 is truthy in Ruby, so it is not defaulted away and does not hit the == 0 special case which requires buffer: :buffered); Concurrent::Channel.new(buffer: :sliding, capacity: -2).","commonSituations":"Capacity read from ENV or config that defaults to 0 or is unset (ENV['N'].to_i -> 0); expecting capacity: 0 alone to mean unbuffered - only the explicit buffer: :buffered, capacity: 0 combination gets that mapping; negative capacities from arithmetic on sizes.","solutions":["Pass a capacity of at least 1 alongside the buffer type: Concurrent::Channel.new(buffer: :dropping, capacity: 5).","For unbuffered semantics use no options at all, or buffer: :unbuffered (or buffer: :buffered, capacity: 0).","Coerce and validate config-derived capacities before Channel.new so misconfiguration fails at startup."],"exampleFix":"# before\nch  = Concurrent::Channel.new(buffer: :dropping)  # no capacity -> raises\nch2 = Concurrent::Channel.new(capacity: 0)        # no buffer key -> raises\n\n# after\nch  = Concurrent::Channel.new(buffer: :dropping, capacity: 5)\nch2 = Concurrent::Channel.new                     # unbuffered","handlingStrategy":"validation","validationCode":"capacity = opts[:capacity] || opts[:size]\nbuffer  = opts[:buffer]\nok = buffer.nil? || buffer == :unbuffered ||\n     (capacity == 0 && buffer == :buffered) ||\n     (capacity && capacity >= 1)\nraise ArgumentError, 'buffered channels need capacity >= 1' unless ok\nch = Concurrent::Channel.new(**opts)","typeGuard":null,"tryCatchPattern":"begin\n  ch = Concurrent::Channel.new(**opts)\nrescue ArgumentError => e\n  raise ConfigError, \"invalid channel options: #{e.message}\"\nend","preventionTips":["Remember 0 is truthy in Ruby: capacity: 0 alone does not become unbuffered - only buffer: :buffered with capacity: 0 does.","Default config-derived capacities explicitly (ENV.fetch('N', 1).to_i) instead of letting nil/0 through.","Cover channel option combinations with a unit test on your construction factory."],"tags":["ruby","concurrent-ruby","channel","buffer","argumenterror","capacity"],"backgroundTag":"channel-buffer-misconfiguration","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}