puppetlabs/puppet · error · Puppet::Error

Could not list gems

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.

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.

Appendix: 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)