{"record":{"id":"c5262ea38f81bad8","repo":"ruby/ruby","slug":"specs-name-from-remote-is-expected-to-be-an","errorCode":null,"errorMessage":"Specs #{name} from #{remote} is expected to be an Array but was unexpected class #{specs.class}","messagePattern":"Specs #(.+?) from #(.+?) is expected to be an Array but was unexpected class #(.+?)","errorType":"exception","errorClass":"MarshalError","httpStatus":null,"severity":"error","filePath":"lib/bundler/rubygems_integration.rb","lineNumber":365,"sourceCode":"      redefine_method(gem_class, :load_plugins) do |*|\n        load_plugin_files specs.flat_map(&:plugins)\n      end\n    end\n\n    def plain_specs\n      Gem::Specification._all\n    end\n\n    def plain_specs=(specs)\n      Gem::Specification.all = specs\n    end\n\n    def fetch_specs(remote, name, fetcher)\n      require \"rubygems/remote_fetcher\"\n      path = remote.uri.to_s + \"#{name}.#{Gem.marshal_version}.gz\"\n      string = fetcher.fetch_path(path)\n      specs = Bundler.safe_load_marshal(string)\n      raise MarshalError, \"Specs #{name} from #{remote} is expected to be an Array but was unexpected class #{specs.class}\" unless specs.is_a?(Array)\n      specs\n    rescue Gem::RemoteFetcher::FetchError\n      # it's okay for prerelease to fail\n      raise unless name == \"prerelease_specs\"\n    end\n\n    def fetch_all_remote_specs(remote, gem_remote_fetcher)\n      specs = fetch_specs(remote, \"specs\", gem_remote_fetcher)\n      pres = fetch_specs(remote, \"prerelease_specs\", gem_remote_fetcher) || []\n\n      specs.concat(pres)\n    end\n\n    def download_gem(spec, uri, cache_dir, fetcher)\n      require \"rubygems/remote_fetcher\"\n      uri = Bundler.settings.mirror_for(uri)\n      redacted_uri = Gem::Uri.redact(uri)\n","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/ruby/ruby/blob/0e5b888e1c355f3f728f2659f085820937dada48/lib/bundler/rubygems_integration.rb#L347-L383","documentation":"Bundler::MarshalError raised in RubygemsIntegration#fetch_specs when the marshaled payload fetched from `#{remote.uri}/specs.#{Gem.marshal_version}.gz` unmarshals successfully but is not an Array. The check guards against gem sources that answer the standard specs endpoint with a well-formed Marshal object of the wrong shape. Note the surrounding rescue only tolerates FetchError for the optional prerelease_specs list; the class check itself is fatal.","triggerScenarios":"A Gemfile source or mirror whose /specs.4.8.gz unmarshals to a Hash or other non-Array object; proxies/servers that re-marshal or rewrite the compact specs payload; calling Bundler.rubygems.fetch_specs(remote, name, fetcher) against a non-conforming host.","commonSituations":"BUNDLE_MIRROR__* env vars or source lines pointing at a broken private server (stale Gemstash, misconfigured Artifactory); a catch-all vhost answering the specs path; CDN edges serving cached, rewritten payloads.","solutions":["Probe the endpoint directly: `curl -s <remote>/specs.4.8.gz | gunzip | head -c 32` — a healthy response starts with the Marshal array marker (\\x04\\b[)","Remove or fix the bad mirror: unset stale BUNDLE_MIRROR__* variables and correct the Gemfile source URL","Repair the private server (update Gemstash/Artifactory, purge its marshaled-specs cache) or fall back to rubygems.org for that host to confirm the diagnosis"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"# probe the source's specs endpoint before pointing bundler at it\nrequire \"open-uri\"\nrequire \"zlib\"\nraw = URI.open(\"#{remote}/specs.#{Gem.marshal_version}.gz\").read\ndata = Bundler.safe_load_marshal(Zlib::Inflate.inflate(raw))\nabort \"#{remote} is not a spec-compliant gem server\" unless data.is_a?(Array)","typeGuard":"def valid_remote_specs?(payload)\n  payload.is_a?(Array) && payload.first.is_a?(Array)\nend","tryCatchPattern":"begin\n  Bundler.definition.resolve\nrescue Bundler::MarshalError => e\n  # e.message names the offending remote; drop the bad mirror/source and retry\n  clean_mirror_config!\n  retry\nend","preventionTips":["Validate private gem servers with the probe above once, in a smoke test, not on every install","Keep BUNDLE_MIRROR__* config in a reviewed file (bundle config) rather than scattered env vars","Upgrade Gemstash/Artifactory in lockstep with RubyGems marshal-version changes"],"tags":["bundler","rubygems","marshal","remote-source","mirrors"],"backgroundTag":"malformed-api-response","analyzedSha":"0e5b888e1c355f3f728f2659f085820937dada48","analyzedAt":"2026-08-21T14:25:43.473Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}