dry-rb/dry-validation
Ractor incompatibility: contracts, schemas, types, and results cannot be used from non-main Ractors on Ruby 4.0
オープン
#749 opened on 2026/05/08
bughelp wanted
Repository metrics
- Stars
- (1,422 個のスター)
- PR merge metrics
- (30d に merged PR はありません)
説明
dry-validation (1.11.1) and its underlying dependencies (dry-schema, dry-types, dry-initializer) cannot be used inside Ractors on Ruby 4.0. No operation works — instantiating contracts, calling them, defining schemas, looking up types, or even passing a validation result into a Ractor.
Environment
- Ruby version: 4.0.3 (2026-04-21)
- dry-validation: 1.11.1
- dry-schema: 1.16.0
- dry-types: 1.9.1
- dry-initializer: 3.2.0
- OS: Linux x86_64
Reproduction
Save as dry_validation_ractor_poc.rb and run with ruby dry_validation_ractor_poc.rb:
#!/usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "dry-validation", "~> 1.10"
end
require "dry-validation"
version = Gem.loaded_specs["dry-validation"]&.version
puts "Ruby #{RUBY_VERSION}, dry-validation #{version}"
puts "-" * 50
class UserContract < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:age).filled(:integer, gt?: 0)
end
rule(:name) { key.failure("too short") if value.length < 2 }
end
contract = UserContract.new
tests = {
"Contract.new in Ractor" => -> { Ractor.new { UserContract.new }.value },
"contract.call in Ractor" => -> { Ractor.new(contract) { |c| c.call(name: "Alice", age: 30) }.value },
"Define contract in Ractor" => -> {
Ractor.new {
Class.new(Dry::Validation::Contract) { params { required(:x).filled(:string) } }.new.call(x: "hi")
}.value
},
"Dry::Types[] in Ractor" => -> { Ractor.new { Dry::Types["strict.string"] }.value },
"Dry::Schema.Params in Ractor"=> -> { Ractor.new { Dry::Schema.Params { required(:a).filled(:string) } }.value },
"Pass result across Ractor" => -> { Ractor.new(contract.call(name: "Alice", age: 30)) { |r| r.success? }.value },
}
tests.each do |name, test|
print "#{name}: "
begin
test.call
puts "OK"
rescue Ractor::RemoteError => e
puts "FAILED — #{e.cause.class}: #{e.cause.message}"
rescue => e
puts "FAILED — #{e.class}: #{e.message}"
end
end
Actual output
Ruby 4.0.3, dry-validation 1.11.1
--------------------------------------------------
Contract.new in Ractor: FAILED — RuntimeError: defined with an un-shareable Proc in a different Ractor
contract.call in Ractor: FAILED — TypeError: allocator undefined for Proc
Define contract in Ractor: FAILED — Ractor::IsolationError: can not get unshareable values from instance variables of classes/modules from non-main Ractors (@dry_initializer from Dry::Validation::Contract)
Dry::Types[] in Ractor: FAILED — Ractor::IsolationError: can not get unshareable values from instance variables of classes/modules from non-main Ractors (@type_map from Dry::Types)
Dry::Schema.Params in Ractor: FAILED — RuntimeError: defined with an un-shareable Proc in a different Ractor
Pass result across Ractor: FAILED — TypeError: allocator undefined for Proc
Expected behavior
At minimum, pre-built contracts should be callable from non-main Ractors, and results should be transferable across Ractor boundaries. Ideally, defining new contracts and looking up types should also work.