puppetlabs/puppet · error · Puppet::Error

Could not list gems: %{detail}

Error message

Could not list gems: %{detail}

What it means

gem.rb:126: the provider lists installed/available gems by shelling out (execute_gem_command, e.g. `gem list ...` or `gem list --remote --source ...`) and wrapping the run; when the gem command exits non-zero (Puppet::ExecutionFailure) it re-raises as Puppet::Error 'Could not list gems: %{detail}' with the command output attached. The listing is what powers instances, prefetch and query, so the error aborts gem package management for the run.

Source

Thrown at lib/puppet/provider/package/gem.rb:126

    if options[:local]
      command_options << "--local"
    else
      command_options << "--remote"
    end
    if options[:source]
      command_options << "--source" << options[:source]
    end
    name = options[:justme]
    if name
      command_options << '\A' + name + '\z'
    end

    begin
      list = execute_gem_command(options[:command], command_options).lines
                                                                    .filter_map { |set| gemsplit(set) }
    rescue Puppet::ExecutionFailure => detail
      raise Puppet::Error, _("Could not list gems: %{detail}") % { detail: detail }, detail.backtrace
    end

    if options[:justme]
      list.shift
    else
      list
    end
  end

  def self.gemsplit(desc)
    # `gem list` when output console has a line like:
    # *** LOCAL GEMS ***
    # but when it's not to the console that line
    # and all blank lines are stripped
    # so we don't need to check for them

    if desc =~ /^(\S+)\s+\((.+)\)/
      gem_name = Regexp.last_match(1)

View on GitHub (pinned to e227c27540)

Solutions

  1. Reproduce as the puppet user with the same binary: `<gemcmd> list` — read %{detail} for the real stderr.
  2. Pin the correct ruby/gem explicitly: set `gemcmd`/`rubygem` command or use `provider => gem` with `command_options`/puppet settings so the intended gem binary is used.
  3. Fix environment issues: correct GEM_HOME/GEM_PATH, repair ownership of the gem home, or update CA certs / credentials for the remote source.
  4. If a remote source is flaky, consider mirroring gems internally instead of ensure => latest against the internet.

Example fix

# before
package { 'bundler': ensure => installed, provider => 'gem' } # picks wrong rvm ruby

# after
package { 'bundler': ensure => installed, provider => 'gem', install_options => ['--bindir', '/usr/local/bin'] }
# plus ensure puppet's gemcmd points at the system ruby (e.g. /usr/bin/gem), not an rvm shim
Defensive patterns

Strategy: try-catch

Validate before calling

# verify the gem binary works as the puppet user
cmd = provider_gemcmd # e.g. /opt/ruby/bin/gem
out = `#{cmd} list 2>&1`
fail "gem broken: #{out}" unless $?.success?

Try / catch

begin
  Puppet::Type.type(:package).provider(:gem).instances
rescue Puppet::Error => e
  raise unless e.message =~ /Could not list gems/
  # skip gem enumeration this run rather than failing the whole catalog
  Puppet.err(e.message); []
end

Prevention

When it happens

Trigger: Any gem package resource (or `puppet resource package provider=gem`) on a node where the configured gemcmd fails: bad gem executable path (wrong ruby via rbenv/rvm shims without the right environment), corrupted gem home/cache permissions, or a remote `--source` that is unreachable/has an expired TLS cert when ensure => latest triggers a remote list.

Common situations: Multi-ruby hosts where puppet picks a different ruby's gem than the app uses; ~/.gem owned by root after someone ran gem with sudo; private gem sources (Gemfury/artifactory) with expired certs or unreachable hosts; gem binary removed during a ruby upgrade.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/6c9db931d27f9682. Report an issue: GitHub.