ruby-concurrency/concurrent-ruby · error · ArgumentError

ImmediateExecutor is not supported

Error message

ImmediateExecutor is not supported

What it means

In the concurrent-ruby-edge actor library, Actor.spawn explicitly rejects ImmediateExecutor with ArgumentError. Actors depend on messages being processed asynchronously on an executor; ImmediateExecutor runs tasks inline on the caller thread, which would break actor isolation and deadlock the mailbox dispatch, so the library refuses it up front instead of failing obscurely later.

Source

Thrown at lib/concurrent-ruby-edge/concurrent/actor.rb:75

    # @example by class and name
    #   Actor.spawn(AdHoc, :ping1) { -> message { message } }
    #
    # @example by option hash
    #   inc2 = Actor.spawn(class:    AdHoc,
    #                      name:     'increment by 2',
    #                      args:     [2],
    #                      executor: Concurrent.global_io_executor) do |increment_by|
    #     lambda { |number| number + increment_by }
    #   end
    #   inc2.ask!(2) # => 4
    #
    # @param block for context_class instantiation
    # @param args see {.to_spawn_options}
    # @return [Reference] never the actual actor
    def self.spawn(*args, &block)
      options = to_spawn_options(*args)
      if options[:executor] && options[:executor].is_a?(ImmediateExecutor)
        raise ArgumentError, 'ImmediateExecutor is not supported'
      end
      if Actor.current
        Core.new(options.merge(parent: Actor.current), &block).reference
      else
        root.ask([:spawn, options, block]).value!
      end
    end

    # as {.spawn} but it'll block until actor is initialized or it'll raise exception on error
    def self.spawn!(*args, &block)
      spawn(to_spawn_options(*args).merge(initialized: future = Concurrent::Promises.resolvable_future), &block).tap { future.wait! }
    end

    # @overload to_spawn_options(context_class, name, *args)
    #   @param [AbstractContext] context_class to be spawned
    #   @param [String, Symbol] name of the instance, it's used to generate the
    #     {Core#path} of the actor
    #   @param args for context_class instantiation

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Drop the :executor option and let actors default to Concurrent.global_io_executor
  2. Use a real pool for actors: executor: Concurrent.new_io_executor(:my_actors) or any ThreadPoolExecutor / Concurrent.global_fast_executor
  3. Keep ImmediateExecutor usage scoped to non-actor code paths, and assert executor type in a spec before spawning

Example fix

# before
Concurrent::Actor.spawn(executor: Concurrent::ImmediateExecutor.new, args: [1]) { |i| i }
# ArgumentError: ImmediateExecutor is not supported

# after
Concurrent::Actor.spawn(executor: Concurrent.global_io_executor, args: [1]) { |i| i }

# or simply omit it
Concurrent::Actor.spawn(args: [1]) { |i| i }
Defensive patterns

Strategy: validation

Validate before calling

if executor.is_a?(Concurrent::ImmediateExecutor)
  raise ArgumentError, 'ImmediateExecutor cannot back actors; use a thread pool'
end
Concurrent::Actor.spawn(executor: executor || Concurrent.global_io_executor, &context)

Type guard

def actor_safe_executor?(exec)
  !exec.is_a?(Concurrent::ImmediateExecutor)
end

Try / catch

begin
  Concurrent::Actor.spawn(spawn_opts, &context)
rescue ArgumentError => e
  raise unless e.message == 'ImmediateExecutor is not supported'
  Concurrent::Actor.spawn(spawn_opts.except(:executor), &context) # fall back to default pool
end

Prevention

When it happens

Trigger: Concurrent::Actor.spawn(executor: Concurrent::ImmediateExecutor.new) { ... } or the equivalent spawn! / to_spawn_options hash carrying :executor; sharing one executor configuration between promises code (where ImmediateExecutor is legal) and actor code.

Common situations: Test suites that globally substitute ImmediateExecutor for determinism and then spawn actors through the same config; performance tuning that tries to eliminate thread hops for lightweight actors; copy-pasting executor setup from promise pipelines into actor spawning.

Related errors


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