ruby-concurrency/concurrent-ruby · error · ArgumentError
probe is not Resolvable
Error message
probe is not Resolvable
What it means
`Ask` is the internal signal ErlangActor uses for request/response: it pairs the caller's message with a `probe` future that gets resolved with the reply. Its constructor enforces `probe.is_a?(Concurrent::Promises::Resolvable)` and raises ArgumentError otherwise. The built-in `ask`/`ask_op` paths construct the probe for you (`ask_op(message, probe = Promises.resolvable_future)`), so hitting this error means a probe of the wrong kind was threaded through — typically a plain `Promises.future` (which is resolvable-scheduled, not a Resolvable) or a custom future object.
Source
Thrown at lib/concurrent-ruby-edge/concurrent/edge/erlang_actor.rb:1264
end
end
private_constant :OnThread
class AbstractSignal < Synchronization::Object
safe_initialization!
end
private_constant :AbstractSignal
class Ask < AbstractSignal
attr_reader :message, :probe
def initialize(message, probe)
super()
@message = message
@probe = probe
raise ArgumentError, 'probe is not Resolvable' unless probe.is_a? Promises::Resolvable
end
end
private_constant :Ask
module HasFrom
# @return [Pid]
attr_reader :from
# @!visibility private
def initialize(from)
# noinspection RubySuperCallWithoutSuperclassInspection
super()
@from = from
end
# @return [true, false]View on GitHub (pinned to 0b88d5ff75)
Solutions
- Use the public `pid.ask(message)` / `pid.ask!` which create a `resolvable_future` probe internally.
- If you call `ask_op` with your own probe, create it with `Concurrent::Promises.resolvable_future` — that object satisfies Promises::Resolvable.
- Bridge custom futures: resolve a `resolvable_future` when your custom future completes, and pass the resolvable as the probe.
- Treat Ask and other private constants as off-limits; go through Pid's public API.
Example fix
// before
probe = Concurrent::Promises.future { compute } # not Resolvable
pid.ask_op(message, probe)
// after
probe = Concurrent::Promises.resolvable_future # a Promises::Resolvable
pid.ask_op(message, probe) Defensive patterns
Strategy: type-guard
Validate before calling
probe = Concurrent::Promises.resolvable_future raise ArgumentError, 'probe is not Resolvable' unless resolvable_probe?(probe) pid.ask_op(message, probe)
Type guard
def resolvable_probe?(obj) obj.is_a?(Concurrent::Promises::Resolvable) end
Prevention
- Prefer the public pid.ask / pid.ask! — they build the resolvable probe for you.
- Create probes only with Concurrent::Promises.resolvable_future.
- Do not construct private signal classes (Ask, AbstractSignal) yourself.
When it happens
Trigger: `pid.ask_op(message, Concurrent::Promises.future { compute })` — a plain future is not a Promises::Resolvable. Monkey-patching or reimplementing ask and passing a Task, an `Edge::Future`, or any custom future class as the probe. Constructing Ask-style signals manually against the internal protocol (Ask itself is a private_constant).
Common situations: Substituting another future implementation for the probe; copying library internals into app code during upgrades; version drift where the resolvable contract on Promises objects changed between edge releases.
Related errors
- environment has to be a class inheriting from Environment or
- only one of block or value can be supplied
- bad options #{options}
- :class option is ignored when calling on context class, use
- unbuffered channels cannot have a capacity
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/d9ca4c7aa08990e7.
Report an issue: GitHub.