ruby-concurrency/concurrent-ruby · error · ArgumentError
#{fallback_policy} is not a valid fallback policy
Error message
#{fallback_policy} is not a valid fallback policy What it means
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.
Source
Thrown at lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb:122
deprecated "#prune_pool has no effect and will be removed in the next release."
end
private
def ns_initialize(opts)
min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
@max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
@synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
@fallback_policy = opts.fetch(:fallback_policy, :abort)
raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if max_length < DEFAULT_MIN_POOL_SIZE
raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if max_length > DEFAULT_MAX_POOL_SIZE
raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if min_length < DEFAULT_MIN_POOL_SIZE
raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length
raise ArgumentError.new("#{fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy)
if @max_queue == 0
if @synchronous
queue = java.util.concurrent.SynchronousQueue.new
else
queue = java.util.concurrent.LinkedBlockingQueue.new
end
else
queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue)
end
@executor = java.util.concurrent.ThreadPoolExecutor.new(
min_length,
max_length,
idletime,
java.util.concurrent.TimeUnit::SECONDS,
queue,
DaemonThreadFactory.new(ns_auto_terminate?),View on GitHub (pinned to 0b88d5ff75)
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
Example fix
# before Concurrent::ThreadPoolExecutor.new(fallback_policy: 'caller_runs') # after Concurrent::ThreadPoolExecutor.new(fallback_policy: :caller_runs)
Defensive patterns
Strategy: validation
Validate before calling
ALLOWED = Concurrent::AbstractExecutorService::FALLBACK_POLICIES
policy = cfg.fetch(:fallback_policy, :abort).to_s.to_sym
raise ArgumentError, "fallback_policy must be one of #{ALLOWED}" unless ALLOWED.include?(policy)
Concurrent::ThreadPoolExecutor.new(fallback_policy: policy) Type guard
def valid_fallback_policy?(v) Concurrent::AbstractExecutorService::FALLBACK_POLICIES.include?(v) end
Try / catch
begin
Concurrent::ThreadPoolExecutor.new(fallback_policy: policy)
rescue ArgumentError => e
raise unless e.message.end_with?('is not a valid fallback policy')
Concurrent::ThreadPoolExecutor.new(fallback_policy: :abort)
end Prevention
- 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
When it happens
Trigger: 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.
Common situations: String/Symbol mismatch from serialized config; copy-paste of policy names from other concurrency libraries; defaults drift between environments where one sets a string.
Related errors
- `synchronous` cannot be set unless `max_queue` is 0
- `max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}
- `max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE
- `min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}
- `min_threads` cannot be more than `max_threads`
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/c1528a67c864dc93.
Report an issue: GitHub.