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

  1. 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
  2. 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
  3. Verify the .gem integrity: re-download it and compare checksums, or run `gem specification pkg.gem` to see if the metadata parses at all
  4. If you control the data producer, ensure the marshal stream only embeds Time, Gem::Specification, and Rational as custom classes
  5. 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

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


AI-assisted analysis of ruby/ruby@0e5b888e1c (2026-08-21). Data as JSON: /api/errors/fd9c4daca652dc4e. Report an issue: GitHub.