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

  1. Use one of the three recognized symbols: :fast, :io, or :immediate.
  2. Pass an ExecutorService instance instead: Concurrent.global_io_executor or your own Concurrent::ThreadPoolExecutor.new(...).
  3. 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

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


AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21). Data as JSON: /api/errors/1af0b6b2ff07e946. Report an issue: GitHub.