Bug #22267
Updated by nobu (Nobuyoshi Nakada) about 1 month ago
On ruby-head, the Active Support (Rails) test suite fails with two Ractor-related errors (rails-nightly build 4651, https://buildkite.com/rails/rails-nightly/builds/4651#01a016ac-eada-492d-9548-44089079b973/L8324).
Rails CI failure. To reproduce on the Rails side, build ruby master, then in a rails/rails checkout run the two tests from activesupport/:
```
$ ruby -v
ruby 4.1.0dev (2026-08-26T07:05:32Z master 346b261abe) +PRISM [x86_64-linux]
$ bundle exec ruby -Ilib:test test/time_zone_test.rb -n test_formatted_offset_in_ractor
#<Thread:...> terminated with exception (report_on_exception is true):
lib/active_support/time_with_zone.rb:78:in 'ActiveSupport::TimeWithZone#period': undefined method 'period_for_utc' for nil (NoMethodError)
from lib/active_support/time_with_zone.rb:139:in 'ActiveSupport::TimeWithZone#zone'
from lib/active_support/time_with_zone.rb:59:in 'ActiveSupport::TimeWithZone#initialize'
from lib/active_support/time_with_zone.rb:535:in 'ActiveSupport::TimeWithZone#marshal_load'
1) Error:
TimeZoneTest#test_formatted_offset_in_ractor:
Ractor::RemoteError: thrown by remote Ractor.
lib/active_support/testing/ractors_assertions.rb:15:in 'Ractor#join'
lib/active_support/testing/ractors_assertions.rb:15:in 'ActiveSupport::Testing::RactorsAssertions#on_ractor'
test/time_zone_test.rb:895:in 'TimeZoneTest#test_formatted_offset_in_ractor'
$ bundle exec ruby -Ilib:test test/notifications_test.rb -n test_record_and_set_subscriptions_when_using_a_regexp
1) Error:
Notifications::NotificationSubscriptionTest#test_record_and_set_subscriptions_when_using_a_regexp:
Ractor::Error: can not copy Hash object.
lib/active_support/ractors.rb:117:in 'Ractor.make_shareable'
lib/active_support/ractors.rb:117:in 'ActiveSupport::Ractors.make_shareable'
lib/active_support/ractors.rb:79:in 'ActiveSupport::Ractors.try_make_shareable'
lib/active_support/notifications.rb:359:in 'ActiveSupport::Notifications.record_subscriptions'
lib/active_support/notifications.rb:314:in 'ActiveSupport::Notifications.subscribe'
test/notifications_test.rb:604:in 'block in <class:NotificationSubscriptionTest>'
```
git bisect points at [GH-18194] #18194 (Per-Ractor GC). Both reduce to Ractor copy and reproduce without Rails.
Steps to reproduce (pure Ruby).
(1) mirrors `TimeZoneTest#test_formatted_offset_in_ractor` TimeZoneTest#test_formatted_offset_in_ractor — an object with a Time ivar (which now forces a Marshal fallback) whose marshal_load reconstructs from state only reachable in the main Ractor:
```ruby ```
class Registry
@zones = { "z" => Object.new }
def self.find(name) = @zones.fetch(name) # reads a class-level ivar
end
class Wrapper
def initialize(name) = (@name = name; @time = Time.now; @zone = Registry.find(name))
def marshal_dump = @name
def marshal_load(name) = (@name = name; @time = Time.now; @zone = Registry.find(name))
end
port = Ractor::Port.new
Ractor.new(port, Wrapper.new("z")) { |p, o| p.send :ok }.join
p port.receive
```
(2) mirrors Notifications...`test_record_and_set_subscriptions_when_using_a_regexp` Notifications...test_record_and_set_subscriptions_when_using_a_regexp — a Hash holding a Set and a shareable Proc:
```ruby ```
require "set"
Ractor.make_shareable({ s: Set.new([1, 2]), p: Ractor.shareable_proc {} }, copy: true)
```
Expected behavior. On Ruby 4.0.6 and before [GH-18194] #18194 (50745e3e48):
- (1) copies natively, `marshal_load` marshal_load is not called, and prints :ok.
- (2) succeeds.
Actual behavior. On current master (346b261abe) and since [GH-18194] #18194 (4f6c34eea6):
(1):
```
#<Thread:...> terminated with exception (report_on_exception is true):
-:3:in 'Registry.find': can not get unshareable values from instance variables of classes/modules from non-main Ractors (@zones from Registry) (Ractor::IsolationError)
from -:8:in 'Wrapper#marshal_load'
-:11:in 'Ractor#join': thrown by remote Ractor. (Ractor::RemoteError)
from -:11:in '<main>'
```
marshal_load now runs in the receiver Ractor, where the object cannot reach main-Ractor state. In Active Support the same happens in `TimeWithZone#marshal_load`; TimeWithZone#marshal_load; there `Time.find_zone` Time.find_zone rescues this and returns nil, so the Rails trace shows `period_for_utc` period_for_utc for nil instead.
(2):
```
-:2:in 'Ractor.make_shareable': can not copy Hash object. (Ractor::Error)
from -:2:in '<main>'
-:2:in 'Ractor.make_shareable': no _dump_data is defined for class Proc (TypeError)
from -:2:in '<main>'
```
[GH-18194]: https://github.com/ruby/ruby/pull/18194