{"record":{"id":"c1528a67c864dc93","repo":"ruby-concurrency/concurrent-ruby","slug":"fallback-policy-is-not-a-valid-fallback-policy-c1528a","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/java_thread_pool_executor.rb","lineNumber":122,"sourceCode":"        deprecated \"#prune_pool has no effect and will be removed in the next release.\"\n      end\n\n      private\n\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(\"`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        raise ArgumentError.new(\"#{fallback_policy} is not a valid fallback policy\") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy)\n\n        if @max_queue == 0\n          if @synchronous\n            queue = java.util.concurrent.SynchronousQueue.new\n          else\n            queue = java.util.concurrent.LinkedBlockingQueue.new\n          end\n        else\n          queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue)\n        end\n\n        @executor = java.util.concurrent.ThreadPoolExecutor.new(\n            min_length,\n            max_length,\n            idletime,\n            java.util.concurrent.TimeUnit::SECONDS,\n            queue,\n            DaemonThreadFactory.new(ns_auto_terminate?),","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb#L104-L140","documentation":"On JRuby, ThreadPoolExecutor validates opts[:fallback_policy] (default :abort) against FALLBACK_POLICY_CLASSES (:abort, :discard, :caller_runs) and raises ArgumentError otherwise. The policy only matters for tasks posted after shutdown; invalid names or Strings fail the check.","triggerScenarios":"Concurrent::ThreadPoolExecutor.new(fallback_policy: :slow) on JRuby; fallback_policy: 'discard' (String, not Symbol); policies read from YAML/JSON/ENV that arrive as strings or with typos.","commonSituations":"String/Symbol mismatch from serialized config; copy-paste of policy names from other concurrency libraries; defaults drift between environments where one sets a string.","solutions":["Use exactly :abort, :discard or :caller_runs as Symbols","Normalize: policy = cfg.fetch(:fallback_policy, :abort).to_s.to_sym","Validate against Concurrent::AbstractExecutorService::FALLBACK_POLICIES before constructing"],"exampleFix":"# before\nConcurrent::ThreadPoolExecutor.new(fallback_policy: 'caller_runs')\n# after\nConcurrent::ThreadPoolExecutor.new(fallback_policy: :caller_runs)","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":["Symbolize serialized policy strings: raw.to_s.to_sym","Validate against FALLBACK_POLICIES in config loading","Remember the ThreadPoolExecutor default is :abort, SingleThreadExecutor's is :discard"],"tags":["ruby","jruby","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"}