ruby/ruby · error · Gem::SafeMarshal::Visitors::ToRuby::MethodCallError
Unable to call #{method.inspect} on #{receiver.inspect}, per
Error message
Unable to call #{method.inspect} on #{receiver.inspect}, perhaps it is a class using marshal compat, which is not visible in ruby? #{e} What it means
RubyGems reads Ruby Marshal 4.8 streams (gem metadata, the specs.4.8.gz source caches, Bundler's marshalled dependency responses) through its SafeMarshal reader and converts them into real objects with the ToRuby visitor. For UserDefined/UserMarshal sections the visitor must dispatch methods like :_load (only Time and Gem::Specification are permitted), :allocate, and :marshal_load on the resolved class; if that call raises a NoMethodError owned by the receiver itself, it is re-raised as Gem::SafeMarshal::Visitors::ToRuby::MethodCallError. The 'marshal compat' wording refers to COMPAT_CLASSES (Rational is rebuilt through an internal RationalCompat that user code never sees).
Source
Thrown at lib/rubygems/safe_marshal/visitors/to_ruby.rb:369
when Elements::SymbolLink
visit_Gem_SafeMarshal_Elements_SymbolLink(element).to_s
else
raise FormatError, "Expected symbol or symbol link, got #{element.inspect} @ #{formatted_stack.join(".")}"
end
end
end
def register_object(o)
@objects << o
o
end
def call_method(receiver, method, *args)
receiver.__send__(method, *args)
rescue NoMethodError => e
raise unless e.receiver == receiver
raise MethodCallError, "Unable to call #{method.inspect} on #{receiver.inspect}, perhaps it is a class using marshal compat, which is not visible in ruby? #{e}"
end
def formatted_stack
formatted = []
@stack[0, @stack_idx].each do |e|
if e.is_a?(Integer)
if formatted.last == "ivar_"
formatted[-1] = "ivar_#{e}"
else
formatted << "[#{e}]"
end
else
formatted << e
end
end
formatted
end
View on GitHub (pinned to 0e5b888e1c)
Solutions
- Clear the stale marshal caches and refetch: `gem sources -u` for the specs cache, remove the copied gem from the install dir, and re-download the .gem from the source
- If it happens during `bundle install`, delete the local marshal cache (e.g. ~/.bundle/cache, vendor/cache copies) so Bundler refetches; Bundler itself falls back to the slower YAML endpoint when it rescues this error
- Verify the .gem integrity: re-download it and compare checksums, or run `gem specification pkg.gem` to see if the metadata parses at all
- If you control the data producer, ensure the marshal stream only embeds Time, Gem::Specification, and Rational as custom classes
- If a local constant shadows a permitted class (e.g. your own Time class), rename it or load the gem metadata in a subprocess without that constant defined
Example fix
# before spec = Gem::SafeMarshal.safe_load(data) # after begin spec = Gem::SafeMarshal.safe_load(data) rescue Gem::SafeMarshal::Visitors::ToRuby::Error => e # cache is unusable; refetch from source like Bundler does spec = refetch_spec_from_source end
Defensive patterns
Strategy: try-catch
Try / catch
begin data = Gem::SafeMarshal.safe_load(marshal_bytes) rescue Gem::SafeMarshal::Visitors::ToRuby::MethodCallError => e # treat source as corrupt: refetch or fall back (this is Bundler's own pattern) data = refetch_or_fallback end
Prevention
- Treat marshal caches (specs.4.8.gz, .gem metadata) as disposable — refetch on any parse failure
- Never let app code define constants named Time, Rational, or Gem::Specification at top level
- Pin rubygems and bundler to compatible versions in CI to keep reader/visitor consistent
- Verify downloads (checksums/size) before caching marshal payloads
When it happens
Trigger: Gem::SafeMarshal.safe_load (called from Gem::Specification.load, Gem::Source#load_specs, Gem::NameTuple.from_list, Bundler.safe_load_marshal) on a stream whose UserDefined/UserMarshal class name resolves to a constant that lacks _load, :allocate, or :marshal_load — e.g. the name resolves to a Module (modules do not respond to allocate), a shadowed constant, or a stub class.
Common situations: Corrupted or truncated specs.4.8.gz cache after a network drop or crash; a partially downloaded .gem whose metadata.gz is cut off; a gem or plugin that defines a top-level constant shadowing Time, Rational, or Gem::Specification; hand-edited or malicious marshal payloads.
Related errors
- wrong @requirements
- Attempting to visit unknown element #{e.inspect}
- invalid Gem::Specification format #{array.inspect}
- 17
- Specs #{name} from #{remote} is expected to be an Array but
AI-assisted analysis of ruby/ruby@0e5b888e1c (2026-08-21).
Data as JSON: /api/errors/fd9c4daca652dc4e.
Report an issue: GitHub.