Project

General

Profile

Actions

Bug #22109

open

Unexpected or misleading error when subclass of a ractor calls a new method on itself

Bug #22109: Unexpected or misleading error when subclass of a ractor calls a new method on itself

Added by miles-georgi (Miles Georgi) 2 days ago. Updated 1 day ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [x86_64-linux]
[ruby-core:125728]

Description

Hey hey!

When I run the following code:

class SubRactor < Ractor
  attr_accessor :foo
end

SubRactor.new { puts foo }.join

I get the following error:

can not access instance variables of shareable objects from non-main Ractors (Ractor::IsolationError)

Unclear if I shouldn't get an error of any sort in this situation, but if this is a legitimate error situation, this error seems inaccurate since Ractors can obviously access instance variables of shareable objects.

A tiny script that shows that:

class C
  attr_accessor :foo

  def initialize = self.foo = 100
  INSTANCE = new.freeze
end

raise "not shareable!" unless Ractor.shareable?(C::INSTANCE)

Ractor.new { puts C::INSTANCE.foo }.join

This prints out 100 as expected instead of giving that error.

This happens on both:

ruby 4.1.0dev (2026-06-11T14:25:18Z master 8e8682fc23) +PRISM [x86_64-linux]

and

ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [x86_64-linux]

The script is so puny seems I shouldn't upload the file but let me know if I should just always upload a script file when filing these even when they are trivially small.

Cheers!

Updated by jhawthorn (John Hawthorn) 2 days ago Actions #1 [ruby-core:125729]

I don't think this has anything to do with the method or the subclass. The message could probably be improved, but the warning exists because instance variables on classes like Ractor (or other objects which are shareable without being frozen) are only accessible by the main Ractor.

❮ ruby -ve 'Ractor.new{ @foo = 123 }.join'
ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [arm64-darwin25]
-e:1: warning: Ractor API is experimental and may change in future versions of Ruby.
#<Thread:0x0000000120db72b8 run> terminated with exception (report_on_exception is true):
-e:1:in 'block in <main>': can not access instance variables of shareable objects from non-main Ractors (Ractor::IsolationError)
-e:1:in 'Ractor#join': thrown by remote Ractor. (Ractor::RemoteError)
        from -e:1:in '<main>'
-e:1:in 'block in <main>': can not access instance variables of shareable objects from non-main Ractors (Ractor::IsolationError)

Updated by miles-georgi (Miles Georgi) 2 days ago · Edited Actions #2 [ruby-core:125731]

Oh good catch that subclassing is irrelevant! Same happens with the simpler Ractor.new { @foo }.join which is a read operation instead of write. However...

instance variables on classes like Ractor (or other objects which are shareable without being frozen) are only accessible by the main Ractor.

this doesn't appear to be true:


class C
  singleton_class.attr_accessor :foo
end

C.foo = 100

Ractor.new { puts C.foo }.join

This prints out 100 instead of giving an error.

The error message itself just doesn't seem to be true. Ractors can access instance variables of shareable objects, classes or otherwise. It seems like if this is a legit error an accurate message should be something like "non-main Ractors cannot access their own instance variables, nor those of any other Ractor"

I'm not sure what class the error should be in case of a non-main Ractor attempting to access its own instance variables since, unless I'm confused, it doesn't seem like an "isolation" issue for a ractor to access its own instance variables.

Updated by byroot (Jean Boussier) 1 day ago Actions #3 [ruby-core:125734]

Ractors can access instance variables of shareable objects, classes or otherwise.

Only if the object the variable points at is itself shareable.

class C
  singleton_class.attr_accessor :foo
end

C.foo = Object.new

Ractor.new { puts C.foo }.join

It is however true we could treat Ractor instances more like classes and allow instance variable access with the same restriction, but it means we need to use RCU when adding or removing instance variables on them, like RClass does.

It's definitely doable, I don't know if it's desirable though.

Updated by miles-georgi (Miles Georgi) 1 day ago · Edited Actions #4 [ruby-core:125741]

byroot (Jean Boussier) wrote in #note-3:

Ractors can access instance variables of shareable objects, classes or otherwise.

Only if the object the variable points at is itself shareable.

Ah, right, true!

It is however true we could treat Ractor instances more like classes and allow instance variable access with the same restriction, but it means we need to use RCU when adding or removing instance variables on them, like RClass does.

It's definitely doable, I don't know if it's desirable though.
Well, to be clear, the thing that surprised me was not that instances of Ractor couldn't access the instance variables of other instances of Ractor. That makes total sense to me. It was that instances of Ractor are not be able to use instance variables at all, ie, their own instance variables (with the exception of the main Ractor, which can, but I'm going to ignore the main Ractor from here-on-out.)

It doesn't seem desirable to me to let instances of Ractor access each other's instance variables but it does seem like it would be desirable to let instances of Ractor access their own instance variables. But if it's not feasible to do that or not desirable, I guess I would then suggest that instances of Ractor should be frozen. I think that would result in a less confusing error message and communicate the restriction better.

Regarding desirability of allowing ractors to make use of instance variables, I could explain what I was up to that led me to attempt that in the first place. Most of the Ractors I've been experimenting with have a similar structure: the block passed to new has some setup with some local variables to hold state and then a loop with a case statement that does different things in different when/in clauses depending on the message received. One of these case statements was getting pretty gigantic so I wanted to organize the logic better. The first thing that made sense to me to try was to move logic out of the when clauses into private helper methods. This though naturally made me move state from local variables in the closure to instance variables. There are other ways I could manage the complexity, so it's not like I can't live without instance variables, but it was natural to try and the misleading error message inspired me to file this issue.

So to recap, I suppose my suggestion would be to either allow instances of Ractor to use their own instance variables or freeze instances of Ractor so that when somebody attempts to assign an instance variable in a ractor they get an error message that results in "oohhhh OK" instead of "huh? that's not true." If that's not doable or desirable then I'd recommend changing the error message to reflect the actual limitation which is that non-main ractors cannot make use of instance variables.

Updated by miles-georgi (Miles Georgi) 1 day ago Actions #5 [ruby-core:125744]

Somebody mentioned to me some potentially annoying aspects of freezing ractor instances like not being able to extend them with convenience methods. So freezing seems like a pretty bad suggestion in retrospect.

So I suspect improving the existing error message when this situation arises might be the path we're on, unless it's feasible and desirable to let ractor instances manage their own instance variables.

Actions

Also available in: PDF Atom