{"record":{"id":"705af9b3025f0d16","repo":"ruby-concurrency/concurrent-ruby","slug":"invalid-action","errorCode":null,"errorMessage":"invalid action","messagePattern":"invalid action","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby-edge/concurrent/channel/selector.rb","lineNumber":25,"sourceCode":"\nmodule Concurrent\n  class Channel\n\n    # @!visibility private\n    class Selector\n\n      def initialize\n        @clauses = []\n        @error_handler = nil\n      end\n\n      def case(channel, action, message = nil, &block)\n        if [:take, :poll, :receive, :~].include?(action)\n          take(channel, &block)\n        elsif [:put, :offer, :send, :<<].include?(action)\n          put(channel, message, &block)\n        else\n          raise ArgumentError.new('invalid action')\n        end\n      end\n\n      def take(channel, &block)\n        raise ArgumentError.new('no block given') unless block_given?\n        @clauses << TakeClause.new(channel, block)\n      end\n      alias_method :receive, :take\n\n      def put(channel, message, &block)\n        @clauses << PutClause.new(channel, message, block)\n      end\n      alias_method :send, :put\n\n      def after(seconds, &block)\n        @clauses << AfterClause.new(seconds, block)\n      end\n      alias_method :timeout, :after","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby-edge/concurrent/channel/selector.rb#L7-L43","documentation":"Selector#case is the generic clause registrar inside Channel.select: it dispatches on the action symbol to a take clause (:take, :poll, :receive, :~) or a put clause (:put, :offer, :send, :<<). Any other action raises, because no clause type exists for it.","triggerScenarios":"sel.case(ch, :read), sel.case(ch, :push), sel.case(ch, 'take') (string instead of symbol), or any action from outside the two whitelists.","commonSituations":"Porting Go or IO idioms and guessing action names (:read/:write/:pop); actions arriving as data from config or a DSL where a string slips through; typos like :recieve or :offre.","solutions":["Use a supported action - take side: :take, :poll, :receive, :~; put side: :put, :offer, :send, :<<.","Or call the specific registrar directly: sel.take(ch) { |v| ... } / sel.put(ch, msg) { ... }.","When the action comes from data, symbolize (.to_sym) and validate against a whitelist before the select block runs."],"exampleFix":"# before\nConcurrent::Channel.select do |sel|\n  sel.case(ch, :read) { |v| handle(v) }   # invalid action\nend\n\n# after\nConcurrent::Channel.select do |sel|\n  sel.case(ch, :take) { |v| handle(v) }\nend","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"VALID_ACTIONS = %i[take poll receive ~ put offer send <<].freeze\n\ndef valid_selector_action?(action)\n  VALID_ACTIONS.include?(action)\nend\n\nsel.case(ch, action) { |v| handle(v) } if valid_selector_action?(action)","tryCatchPattern":"begin\n  sel.case(ch, action) { |v| handle(v) }\nrescue ArgumentError => e\n  raise ArgumentError, \"unsupported action #{action.inspect}; use one of #{VALID_ACTIONS}\"\nend","preventionTips":["Symbols only - symbolize strings from config before they reach sel.case.","Consider calling sel.take/sel.put directly; the generic #case adds a dispatch layer that invites typos.","Whitelist-validate DSL inputs at the boundary."],"tags":["ruby","concurrent-ruby","selector","channel","argumenterror","enum"],"backgroundTag":"invalid-enum-value","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}