ruby-concurrency/concurrent-ruby · error · ArgumentError
executor not recognized by '#{executor_identifier}'
Error message
executor not recognized by '#{executor_identifier}' What it means
Concurrent::Options.executor (options.rb:33-41) normalizes the executor: argument accepted by Promises, Agent, Channel and related APIs. It recognizes only the symbols :fast, :io, :immediate, or an object for which executor_identifier.is_a?(Concurrent::ExecutorService) is true; anything else — an unknown symbol, a String, a class/module — raises ArgumentError "executor not recognized by '...'".
Source
Thrown at lib/concurrent-ruby/concurrent/options.rb:38
if identifier = opts.fetch(:executor, nil)
executor(identifier)
else
nil
end
end
def self.executor(executor_identifier)
case executor_identifier
when :fast
Concurrent.global_fast_executor
when :io
Concurrent.global_io_executor
when :immediate
Concurrent.global_immediate_executor
when Concurrent::ExecutorService
executor_identifier
else
raise ArgumentError, "executor not recognized by '#{executor_identifier}'"
end
end
end
end
View on GitHub (pinned to 0b88d5ff75)
Solutions
- Use one of the three recognized symbols: :fast, :io, or :immediate.
- Pass an ExecutorService instance instead: Concurrent.global_io_executor or your own Concurrent::ThreadPoolExecutor.new(...).
- Whitelist and symbolize config-sourced executor names before they reach the API.
Example fix
# before
Concurrent::Promises.future(executor: 'io') { work } # ArgumentError: executor not recognized by 'io'
# after
Concurrent::Promises.future(executor: :io) { work }
# or
Concurrent::Promises.future(executor: Concurrent.global_io_executor) { work } Defensive patterns
Strategy: validation
Validate before calling
ALLOWED_EXECUTORS = %i[fast io immediate].freeze
def resolve_executor(value)
return value if value.is_a?(Concurrent::ExecutorService)
return Concurrent::Options.executor(value) if ALLOWED_EXECUTORS.include?(value)
raise ArgumentError, "unknown executor #{value.inspect} (use :fast, :io, :immediate, or an ExecutorService)"
end Try / catch
begin
Concurrent::Promises.future(executor: cfg_executor) { work }
rescue ArgumentError
Concurrent::Promises.future(executor: :io) { work } # safe default
end Prevention
- Whitelist executor names at the config boundary; reject unknown strings early.
- Prefer passing ExecutorService instances (e.g. Concurrent.global_io_executor) over symbols from config.
- Symbolize stringly-typed executor config before it reaches Promises/Agent.
When it happens
Trigger: Concurrent::Promises.future(executor: :quick) { 1 } (unknown symbol); executor: 'io' (String instead of Symbol); executor: 'global_io_executor' or a Symbol loaded from YAML/user input and passed through unvalidated.
Common situations: Typos in executor symbols; stringly-typed configuration that is not symbolized or whitelisted; assuming arbitrary executor names are registered globally like Spring bean names.
Related errors
- number of threads must be greater than zero
- #{@fallback_policy} is not a valid fallback policy
- no block given
- no block given
- no block given
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/1af0b6b2ff07e946.
Report an issue: GitHub.