Bug #21360
openInconsistent Support for `Exception#cause` in `Fiber#raise` and `Thread#raise`
Description
The raise
method supports setting the cause of an exception using the cause:
keyword, but this behavior does not work as expected when calling Fiber#raise
or Thread#raise
, resulting in a TypeError
. This breaks consistency with Kernel#raise
and makes it difficult to attach causal chains to exceptions raised from other execution contexts.
The Problem¶
The following code behaves correctly when using Kernel#raise
, correctly setting the cause
:
cause = RuntimeError.new("cause")
begin
raise RuntimeError, "boom", cause: cause
rescue => error
pp error: error, cause: error.cause
end
Produces:
{error: #<RuntimeError: boom>, cause: #<RuntimeError: cause>}
However, using Fiber.current.raise
or Thread.current.raise
with the same arguments produces a TypeError
:
begin
Fiber.current.raise RuntimeError, "boom", cause: cause
rescue => error
pp error: error, cause: error.cause
end
Results in:
{error: #<TypeError: backtrace must be an Array of String or an Array of Thread::Backtrace::Location>, cause: nil}
This occurs because the third argument is incorrectly interpreted as a backtrace, not as keyword arguments. A similar issue occurs with Thread#raise
.
Proposed Solution¶
Update Fiber#raise
and Thread#raise
to accept and correctly interpret keyword arguments, including cause:
, in the same manner as Kernel#raise
. This would restore consistency across all raise
implementations and allow causal exception chaining regardless of context. In other words, Fiber#raise
and Thread#raise
would be defined as the same interface as Kernel#raise
.
Updated by Eregon (Benoit Daloze) 1 day ago
- Related to Feature #21359: Introduce `Exception#cause=` for Post-Initialization Assignment added