{"record":{"id":"d89389d2dfc27ed6","repo":"ruby-concurrency/concurrent-ruby","slug":"unbuffered-channels-cannot-have-a-capacity","errorCode":null,"errorMessage":"unbuffered channels cannot have a capacity","messagePattern":"unbuffered channels cannot have a capacity","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby-edge/concurrent/channel.rb","lineNumber":58,"sourceCode":"    def_delegators :buffer,\n      :size, :capacity, :close, :closed?,\n      :blocking?, :empty?, :full?\n\n    alias_method :length, :size\n    alias_method :stop, :close\n\n    def initialize(opts = {})\n      # 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)","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby-edge/concurrent/channel.rb#L40-L76","documentation":"Concurrent::Channel.new builds the channel's buffer from the :buffer/:capacity options. An unbuffered channel is a pure rendezvous between sender and receiver with no storage, so a capacity is meaningless; passing both buffer: :unbuffered and a capacity (also accepted under the :size alias) is rejected with this ArgumentError.","triggerScenarios":"Concurrent::Channel.new(buffer: :unbuffered, capacity: 10), or via the alias Concurrent::Channel.new(size: 3, buffer: :unbuffered). Any truthy capacity together with buffer: :unbuffered triggers it.","commonSituations":"Switching a channel from buffered to unbuffered in a shared config without removing the capacity key; YAML/ENV-driven channel options where capacity is defaulted for every channel; copy-pasting option hashes between channel definitions.","solutions":["Drop the capacity: Concurrent::Channel.new(buffer: :unbuffered), or simply Concurrent::Channel.new which is unbuffered by default.","If you actually want bounded storage, use buffer: :buffered (or :dropping/:sliding) with a capacity of at least 1.","Validate channel option hashes once at config-load time instead of deep inside worker code."],"exampleFix":"# before\nch = Concurrent::Channel.new(buffer: :unbuffered, capacity: 10)\n\n# after\nch = Concurrent::Channel.new(buffer: :unbuffered)\n# or, if capacity is actually wanted:\nch = Concurrent::Channel.new(buffer: :buffered, capacity: 10)","handlingStrategy":"validation","validationCode":"def channel_opts_conflict?(opts)\n  (opts[:capacity] || opts[:size]) && opts[:buffer] == :unbuffered\nend\n\nraise ArgumentError, 'drop :capacity when buffer is :unbuffered' if channel_opts_conflict?(opts)\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":["Centralize channel construction in one factory that validates the option hash.","Remember :size is an alias of :capacity - check both keys when sanitizing config.","Document each channel's intended buffer type at its definition site."],"tags":["ruby","concurrent-ruby","channel","buffer","argumenterror","goroutine-channels"],"backgroundTag":"channel-buffer-misconfiguration","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}