Skip to content

circular causes when re-raising an exception originally raised with the two-argument raise Class, arg form (regression in 10.1.1.0) #9551

Description

@dazuma

NOTE: Claude Opus 5 did this investigation and wrote up the following report, while investigating the impact of this issue on a separate project.

This looks like a residual case of #9398. The fix in #9407 resolved the originally reported cases but appears to have moved the failure onto a neighboring path rather than eliminating it.


Re-raising a previously-rescued exception raises ArgumentError: circular causes on JRuby 10.1.1.0, when that exception was originally raised using the two-argument raise Class, arg form. CRuby returns the original exception. JRuby 10.0.4.0 and 10.1.0.0 also return the original exception, so this is new in 10.1.1.0.

This is the same nested-rescue shape as #9398, but with the inverse trigger: #9398 was about exceptions raised implicitly by the VM, which 10.1.1.0 now handles correctly. The explicitly-raised two-argument case regressed in the process.

Environment Information

jruby 10.1.1.0 (4.0.0) 2026-07-22 aaa95d57e1 Java HotSpot(TM) 64-Bit Server VM 24.0.1+9-30 on 24.0.1+9-30 +indy +jit [arm64-darwin]

Compared against:

ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +PRISM [arm64-darwin23]
jruby 10.0.4.0 (3.4.5) 2026-03-03 9af05b916f Java HotSpot(TM) 64-Bit Server VM 24.0.1+9-30 on 24.0.1+9-30 +indy +jit [arm64-darwin]
jruby 10.1.0.0 (4.0.0) 2026-04-20 32f988b78c Java HotSpot(TM) 64-Bit Server VM 24.0.1+9-30 on 24.0.1+9-30 +indy +jit [arm64-darwin]

Expected Behavior

The minimal case below should propagate RuntimeError: inner, as it does on CRuby 4.0, JRuby 10.0.4.0, and JRuby 10.1.0.0.

begin
  raise RuntimeError, "inner"      # explicit two-argument raise
rescue RuntimeError => inner
  begin
    raise ArgumentError, "wrapper" # nested raise; its cause is `inner`
  rescue ArgumentError
    raise inner                    # re-raise the outer-rescued exception
  end
end

Actual Behavior

On JRuby 10.1.1.0 the raise inner line raises ArgumentError: circular causes.

$ ruby minimal.rb                     # CRuby 4.0.6
minimal.rb:2:in '<main>': inner (RuntimeError)

$ jruby minimal.rb                    # 10.0.4.0 and 10.1.0.0
RuntimeError: inner
  <main> at minimal.rb:2

$ jruby minimal.rb                    # 10.1.1.0
ArgumentError: circular causes
  <main> at minimal.rb:7

Workaround

Passing an explicit cause: nil on the re-raise avoids it, and behaves identically on CRuby and all tested versions of JRuby:

raise inner, cause: nil

This suppresses only the implicit adoption of $! as the cause. It does not clear a cause the exception already carries, so an existing cause chain is preserved on both runtimes.

Notes

Some observations that may help narrow this down.

It depends on the raise form, not the exception class. Only the two-argument raise Class, arg form triggers it. raise Class.new(arg), raise Class, and raise "string" are all unaffected, as are exceptions raised implicitly by the VM.

Within that form, every class I tried is affected, including NameError and ArgumentError — the two classes named in the #9407 discussion as having gained explicit cause handling. So the fix appears to have addressed the implicit/VM-raised path specifically, leaving the explicit two-argument path still not recording a cause.

That matches the mechanism described in #9398: an exception that does not get a nil cause recorded early enough will later try to implicitly adopt $! as its cause, and errors out when that turns out to be circular instead of declining silently as CRuby does.

Also note: I bisected this to the 10.1.0.0 → 10.1.1.0 release window, not to an individual commit. The attribution to #9407 is inferred from that window and from the discussion in #9398, so it is worth confirming.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions