ruby-concurrency/concurrent-ruby · error · Concurrent::Error
it is not rejected
Error message
it is not rejected
What it means
Future#exception (promises.rb:1011-1016) exists so a rejected future can be used with raise (Ruby's raise calls obj.exception). It requires the future to be rejected; when called on a pending or fulfilled future it raises Concurrent::Error 'it is not rejected' (promises.rb:1015). It is a state precondition on the future, not a general accessor for the result.
Source
Thrown at lib/concurrent-ruby/concurrent/promises.rb:1015
# @raise [Exception] {#reason} on rejection
def value!(timeout = nil, timeout_value = nil)
if wait_until_resolved! timeout
internal_state.value
else
timeout_value
end
end
# Allows rejected Future to be risen with `raise` method.
# If the reason is not an exception `Runtime.new(reason)` is returned.
#
# @example
# raise Promises.rejected_future(StandardError.new("boom"))
# raise Promises.rejected_future("or just boom")
# @raise [Concurrent::Error] when raising not rejected future
# @return [Exception]
def exception(*args)
raise Concurrent::Error, 'it is not rejected' unless rejected?
raise ArgumentError unless args.size <= 1
reason = Array(internal_state.reason).flatten.compact
callsites = SET_BACKTRACE_LOCATIONS_SUPPORTED ? caller_locations : caller
if reason.size > 1
ex = Concurrent::MultipleErrors.new reason
ex.set_backtrace(callsites)
ex
else
ex = if reason[0].respond_to? :exception
reason[0].exception(*args)
else
RuntimeError.new(reason[0]).exception(*args)
end
if SET_BACKTRACE_LOCATIONS_SUPPORTED && (locations = ex.backtrace_locations)
ex.set_backtrace locations + callsites
else
ex.set_backtrace Array(ex.backtrace) + callsites.map(&:to_s)
endView on GitHub (pinned to 0b88d5ff75)
Solutions
- Check state first: future.wait!; raise future only if future.rejected?.
- Prefer future.value! — it returns the value when fulfilled and raises the rejection reason when rejected, no precondition needed.
- Use future.result ([:ok/:error, value, reason]) for explicit branching instead of raise.
Example fix
# before raise future # Concurrent::Error: it is not rejected (future was fulfilled) # after future.wait! raise future if future.rejected? # or simply: future.value! # returns value, or raises the rejection reason
Defensive patterns
Strategy: validation
Validate before calling
future.wait! raise future.exception if future.rejected? # only raise after confirming rejection
Prevention
- Branch on rejected?/fulfilled? before converting a future to an exception.
- Prefer future.value! or future.result over raise future for outcome propagation.
- Ensure futures are resolved (wait!) before inspecting their state in assertions.
When it happens
Trigger: raise future where the future fulfilled successfully; future.exception called explicitly on a pending or fulfilled future; generic error-propagation code that re-raises whatever future it receives without checking rejected?.
Common situations: Writing `raise future` plumbing in rescue handlers; helpers that turn outcomes into exceptions without branching on state; testing rejection paths with futures that actually fulfilled.
Related errors
- no block given
- supported only on root promise
- ArgumentError
- no block given
- levels has to be higher than 0
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/1cb816d5d8046330.
Report an issue: GitHub.