{"record":{"id":"5961729f8abead81","repo":"ruby-concurrency/concurrent-ruby","slug":"fallback-policy-is-not-a-valid-fallback-policy-596172","errorCode":null,"errorMessage":"#{@fallback_policy} is not a valid fallback policy","messagePattern":"#(.+?) is not a valid fallback policy","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb","lineNumber":155,"sourceCode":"\n    # @!macro thread_pool_executor_method_prune_pool\n    def prune_pool\n      deprecated \"#prune_pool has no effect and will be removed in next the release, see https://github.com/ruby-concurrency/concurrent-ruby/pull/1082.\"\n    end\n\n    private\n\n    # @!visibility private\n    def ns_initialize(opts)\n      @min_length      = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i\n      @max_length      = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i\n      @idletime        = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i\n      @max_queue       = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i\n      @synchronous     = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)\n      @fallback_policy = opts.fetch(:fallback_policy, :abort)\n\n      raise ArgumentError.new(\"`synchronous` cannot be set unless `max_queue` is 0\") if @synchronous && @max_queue > 0\n      raise ArgumentError.new(\"#{@fallback_policy} is not a valid fallback policy\") unless FALLBACK_POLICIES.include?(@fallback_policy)\n      raise ArgumentError.new(\"`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}\") if @max_length < DEFAULT_MIN_POOL_SIZE\n      raise ArgumentError.new(\"`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}\") if @max_length > DEFAULT_MAX_POOL_SIZE\n      raise ArgumentError.new(\"`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}\") if @min_length < DEFAULT_MIN_POOL_SIZE\n      raise ArgumentError.new(\"`min_threads` cannot be more than `max_threads`\") if min_length > max_length\n\n      @pool                 = [] # all workers\n      @ready                = [] # used as a stash (most idle worker is at the start)\n      @queue                = [] # used as queue\n      # @ready or @queue is empty at all times\n      @scheduled_task_count = 0\n      @completed_task_count = 0\n      @largest_length       = 0\n      @workers_counter      = 0\n      @ruby_pid             = $$ # detects if Ruby has forked\n    end\n\n    # @!visibility private\n    def ns_limited_queue?","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb#L137-L173","documentation":"On MRI, ThreadPoolExecutor validates opts[:fallback_policy] (default :abort) against FALLBACK_POLICIES (:abort, :discard, :caller_runs) and raises ArgumentError otherwise. The policy governs tasks posted after shutdown; Symbols are required, so Strings and typos fail.","triggerScenarios":"Concurrent::ThreadPoolExecutor.new(fallback_policy: 'discard') on MRI; fallback_policy: :ignore (typo/invalid); values loaded from YAML, JSON or ENV that arrive as strings.","commonSituations":"Serialized config producing Strings; policy names copied from other libraries; per-environment defaults where one environment has a typo.","solutions":["Use exactly :abort, :discard or :caller_runs as Symbols","Normalize strings early: policy = raw.to_s.downcase.to_sym","Validate against Concurrent::AbstractExecutorService::FALLBACK_POLICIES before constructing"],"exampleFix":"# before\nConcurrent::ThreadPoolExecutor.new(fallback_policy: 'discard')\n# after\nConcurrent::ThreadPoolExecutor.new(fallback_policy: :discard)","handlingStrategy":"validation","validationCode":"ALLOWED = Concurrent::AbstractExecutorService::FALLBACK_POLICIES\npolicy = cfg.fetch(:fallback_policy, :abort).to_s.to_sym\nraise ArgumentError, \"fallback_policy must be one of #{ALLOWED}\" unless ALLOWED.include?(policy)\nConcurrent::ThreadPoolExecutor.new(fallback_policy: policy)","typeGuard":"def valid_fallback_policy?(v)\n  Concurrent::AbstractExecutorService::FALLBACK_POLICIES.include?(v)\nend","tryCatchPattern":"begin\n  Concurrent::ThreadPoolExecutor.new(fallback_policy: policy)\nrescue ArgumentError => e\n  raise unless e.message.end_with?('is not a valid fallback policy')\n  Concurrent::ThreadPoolExecutor.new(fallback_policy: :abort)\nend","preventionTips":["Convert YAML/JSON/ENV strings to Symbols at load time","Test config parsing with string values to catch Symbol/String drift","Validate the whole opts hash once before pool construction"],"tags":["ruby","concurrency","thread-pool","argument-error","configuration"],"backgroundTag":"invalid-fallback-policy","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}