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
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.
Source
Thrown at lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb:26
# @!macro single_thread_executor
# @!macro abstract_executor_service_public_api
# @!visibility private
class JavaSingleThreadExecutor < JavaExecutorService
include SerialExecutorService
# @!macro single_thread_executor_method_initialize
def initialize(opts = {})
super(opts)
end
private
def ns_initialize(opts)
@executor = java.util.concurrent.Executors.newSingleThreadExecutor(
DaemonThreadFactory.new(ns_auto_terminate?)
)
@fallback_policy = opts.fetch(:fallback_policy, :discard)
raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.keys.include?(@fallback_policy)
end
end
end
end
View on GitHub (pinned to 0b88d5ff75)
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
Example fix
# before Concurrent::SingleThreadExecutor.new(fallback_policy: 'abort') # after policy = 'abort'.to_sym Concurrent::SingleThreadExecutor.new(fallback_policy: policy)
Defensive patterns
Strategy: validation
Validate before calling
ALLOWED = Concurrent::AbstractExecutorService::FALLBACK_POLICIES # [:abort, :discard, :caller_runs]
policy = cfg.fetch(:fallback_policy, :discard).to_s.to_sym
raise ArgumentError, "fallback_policy must be one of #{ALLOWED}, got #{policy.inspect}" unless ALLOWED.include?(policy)
Concurrent::SingleThreadExecutor.new(fallback_policy: policy) Type guard
def valid_fallback_policy?(v) Concurrent::AbstractExecutorService::FALLBACK_POLICIES.include?(v) end
Try / catch
begin
Concurrent::SingleThreadExecutor.new(fallback_policy: policy)
rescue ArgumentError => e
raise unless e.message.end_with?('is not a valid fallback policy')
Concurrent::SingleThreadExecutor.new(fallback_policy: :discard)
end Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- number of threads must be greater than zero
- no block given
- `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
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/a127aa7d3259443e.
Report an issue: GitHub.