ruby-concurrency/concurrent-ruby · error · ArgumentError
no action given
Error message
no action given
What it means
Every Agent action is dispatched through enqueue_action_job, which requires a non-nil action - a proc/lambda/method object, or the block that Agent#send converts for you. A nil action means there is nothing to execute, so it raises ArgumentError before the job is enqueued.
Source
Thrown at lib/concurrent-ruby/concurrent/agent.rb:511
if @error_mode && !ERROR_MODES.include?(@error_mode)
raise ArgumentError.new('unrecognized error mode')
elsif @error_mode.nil?
@error_mode = @error_handler ? :continue : :fail
end
@error_handler ||= DEFAULT_ERROR_HANDLER
@validator = opts.fetch(:validator, DEFAULT_VALIDATOR)
@current = Concurrent::AtomicReference.new(initial)
@error = Concurrent::AtomicReference.new(nil)
@caller = Concurrent::ThreadLocalVar.new(nil)
@queue = []
self.observers = Collection::CopyOnNotifyObserverSet.new
end
def enqueue_action_job(action, args, executor)
raise ArgumentError.new('no action given') unless action
job = Job.new(action, args, executor, @caller.value || Thread.current.object_id)
synchronize { ns_enqueue_job(job) }
end
def enqueue_await_job(latch)
synchronize do
if (index = ns_find_last_job_for_thread)
job = Job.new(AWAIT_ACTION, [latch], Concurrent.global_immediate_executor,
Thread.current.object_id)
ns_enqueue_job(job, index+1)
else
latch.count_down
true
end
end
end
def ns_enqueue_job(job, index = nil)View on GitHub (pinned to 0b88d5ff75)
Solutions
- Check the action before dispatching: agent.send(action) if action.
- Fail loudly at lookup time: action = handlers.fetch(step) { raise KeyError, "no handler for #{step}" } instead of silently sending nil.
- When there is no named action, use the block form: agent.send { |old_value| compute(old_value) }.
Example fix
# before
agent.send(handlers[step]) # handlers[step] is nil for unknown steps
# after
action = handlers.fetch(step) { raise KeyError, "no handler for #{step}" }
agent.send(action) Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, "no action for #{step.inspect}" if action.nil?
agent.send(action) if action Try / catch
begin
agent.send(action, *args)
rescue ArgumentError => e
raise ConfigError, "agent dispatch failed: #{e.message}"
end Prevention
- Use fetch with a raise when looking up actions from strategy maps - never send a possibly-nil lookup.
- Prefer the block form agent.send { |old| ... } when no named action exists.
- Validate handler tables at startup, not at dispatch time.
When it happens
Trigger: agent.send(nil); agent.send(handlers[step]) where the lookup returned nil for an unknown step; dynamically built actions (strategy maps, 'do_#{step}'.to_sym lookups, conditional procs like cond ? proc {...} : nil) dispatched without a nil check.
Common situations: Actions built from a lookup table or strategy hash with missing keys; passing opts[:handler] that was never set; conditional action construction where one branch returns nil but dispatch happens unconditionally.
Related errors
- unrecognized error mode
- unbuffered channels cannot have a capacity
- capacity must be at least 1 for this buffer type
- no block given
- size must be greater than 0
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/2b31d12c152ae013.
Report an issue: GitHub.