{"record":{"id":"a127aa7d3259443e","repo":"ruby-concurrency/concurrent-ruby","slug":"fallback-policy-is-not-a-valid-fallback-policy","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_single_thread_executor.rb","lineNumber":26,"sourceCode":"    # @!macro single_thread_executor\n    # @!macro abstract_executor_service_public_api\n    # @!visibility private\n    class JavaSingleThreadExecutor < JavaExecutorService\n      include SerialExecutorService\n\n      # @!macro single_thread_executor_method_initialize\n      def initialize(opts = {})\n        super(opts)\n      end\n\n      private\n\n      def ns_initialize(opts)\n        @executor = java.util.concurrent.Executors.newSingleThreadExecutor(\n            DaemonThreadFactory.new(ns_auto_terminate?)\n        )\n        @fallback_policy = opts.fetch(:fallback_policy, :discard)\n        raise ArgumentError.new(\"#{@fallback_policy} is not a valid fallback policy\") unless FALLBACK_POLICY_CLASSES.keys.include?(@fallback_policy)\n      end\n    end\n  end\nend\n","sourceCodeStart":8,"sourceCodeEnd":31,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb#L8-L31","documentation":"SingleThreadExecutor on JRuby accepts opts[:fallback_policy], which decides what happens to tasks posted after shutdown. Valid values are the FALLBACK_POLICY_CLASSES keys (:abort, :discard, :caller_runs) and the default is :discard. ns_initialize raises ArgumentError when the policy is anything else, including Strings.","triggerScenarios":"Concurrent::SingleThreadExecutor.new(fallback_policy: :slow) (typo); fallback_policy: 'abort' (String instead of Symbol); policy values parsed from YAML/JSON/ENV that arrive as strings.","commonSituations":"String/Symbol mismatch from configuration files; typos in policy names; reusing policy names valid in other libraries (e.g. :delay) that do not exist here.","solutions":["Use exactly :abort, :discard or :caller_runs as Symbols","Normalize config before constructing: policy = cfg.fetch(:fallback_policy, :discard).to_s.to_sym","Validate against Concurrent::AbstractExecutorService::FALLBACK_POLICIES and fail fast with the allowed list"],"exampleFix":"# before\nConcurrent::SingleThreadExecutor.new(fallback_policy: 'abort')\n# after\npolicy = 'abort'.to_sym\nConcurrent::SingleThreadExecutor.new(fallback_policy: policy)","handlingStrategy":"validation","validationCode":"ALLOWED = Concurrent::AbstractExecutorService::FALLBACK_POLICIES # [:abort, :discard, :caller_runs]\npolicy = cfg.fetch(:fallback_policy, :discard).to_s.to_sym\nraise ArgumentError, \"fallback_policy must be one of #{ALLOWED}, got #{policy.inspect}\" unless ALLOWED.include?(policy)\nConcurrent::SingleThreadExecutor.new(fallback_policy: policy)","typeGuard":"def valid_fallback_policy?(v)\n  Concurrent::AbstractExecutorService::FALLBACK_POLICIES.include?(v)\nend","tryCatchPattern":"begin\n  Concurrent::SingleThreadExecutor.new(fallback_policy: policy)\nrescue ArgumentError => e\n  raise unless e.message.end_with?('is not a valid fallback policy')\n  Concurrent::SingleThreadExecutor.new(fallback_policy: :discard)\nend","preventionTips":["Symbolize policy values at the config boundary: raw.to_s.to_sym","Spell-check against FALLBACK_POLICIES in config specs","Never mix Strings and Symbols for policy keys across environments"],"tags":["ruby","jruby","concurrency","executor","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"}