puppetlabs/puppet · error · Puppet::Error

puppet:// URLs are not supported as gem sources

Error message

puppet:// URLs are not supported as gem sources

What it means

gem.rb:240: while building gem install options, the provider parses the resource's source as a URI; a scheme of 'puppet' (puppet:// URLs from the file server) is explicitly rejected with Puppet::Error, because gem cannot fetch packages from Puppet's fileserver. Supported shapes are no scheme (local path), file:// paths, absolute paths, or a remote repository URL passed via --source.

Source

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

    end

    source = resource[:source]
    if source
      begin
        uri = URI.parse(source)
      rescue => detail
        self.fail Puppet::Error, _("Invalid source '%{uri}': %{detail}") % { uri: uri, detail: detail }, detail
      end

      case uri.scheme
      when nil
        # no URI scheme => interpret the source as a local file
        command_options << source
      when /file/i
        command_options << uri.path
      when 'puppet'
        # we don't support puppet:// URLs (yet)
        raise Puppet::Error, _("puppet:// URLs are not supported as gem sources")
      else
        # check whether it's an absolute file path to help Windows out
        if Puppet::Util.absolute_path?(source)
          command_options << source
        else
          # interpret it as a gem repository
          command_options << "--source" << source.to_s << resource[:name]
        end
      end
    else
      command_options << resource[:name]
    end

    output = self.class.execute_gem_command(command, command_options)
    # Apparently some gem versions don't exit non-0 on failure.
    self.fail _("Could not install: %{output}") % { output: output.chomp } if output.include?("ERROR")
  end

View on GitHub (pinned to e227c27540)

Solutions

  1. Stage the .gem with a file resource from puppet://, then pass the local path as the gem package's source.
  2. Or serve gems from a real repository (file:// directory, https internal geminabox/artifactory) and point source at it.
  3. Never use a puppet:// URL directly as a gem source — the provider rejects it by design.

Example fix

# before
package { 'foo': ensure => installed, provider => 'gem', source => 'puppet:///modules/gems/foo-1.0.gem' }

# after
file { '/tmp/foo-1.0.gem': source => 'puppet:///modules/gems/foo-1.0.gem', before => Package['foo'] }
package { 'foo': ensure => installed, provider => 'gem', source => '/tmp/foo-1.0.gem' }
Defensive patterns

Strategy: validation

Validate before calling

# reject puppet:// schemes before declaring the resource
uri = URI.parse(source.to_s)
fail 'gem sources cannot be puppet:// — stage the file first' if uri.scheme == 'puppet'

Type guard

def valid_gem_source?(s)
  u = URI.parse(s.to_s)
  u.scheme.nil? || u.scheme =~ /file/i || %w[http https].include?(u.scheme)
rescue URI::InvalidURIError
  false
end

Try / catch

begin
  provider.install
rescue Puppet::Error => e
  raise unless e.message.include?('puppet:// URLs are not supported')
  # stage from the fileserver, then install from the local copy
  stage(source); resource[:source] = local_path; retry
end

Prevention

When it happens

Trigger: package { X: provider => gem, source => 'puppet:///modules/gems/x-1.0.gem' } — any attempt to install a .gem directly from the module mount. The URI parses fine (it was already validated as a URI), only the scheme check fails.

Common situations: Teams air-gapping gems try to serve them from the Puppet fileserver the same way they stage debs/rpms; converting a file { source => 'puppet://...' } pattern to package resources without adapting the URL.

Related errors


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