puppetlabs/puppet · error · Puppet::Forge::Errors::CommunicationError

Unable to connect to the server at

Error message

Unable to connect to the server at %{uri}. Detail: %{detail}.

What it means

set_sensitive_parameters runs when a resource declares sensitive => [name, ...]. For each name Puppet looks up the attribute: properties get sensitive = true, parameters get the adjacent cannot-redact warning, and this err fires when self.class.attrclass(name) is nil - the name is not defined on the resource type at all, most often a typo or an attribute borrowed from a different type. The consequence is worse than cosmetic: because the property you meant to redact was never marked, its value keeps appearing in logs and reports in clear text.

Solutions

  1. Get the exact attribute list for the type: puppet describe user (or puppet describe -s user) and match spelling and case.
  2. Fix the manifest entry to the real property name, then re-run with --noop and confirm the err is gone.
  3. Apply the rule: only properties (state the provider syncs, like user#password) can be marked sensitive; parameters get the warning branch instead.
  4. For custom types, declare the attribute with newproperty(:name) so attrclass resolves.
  5. Audit earlier logs and reports for the clear-text value - runs before the fix logged it unredacted.

Example fix

# before: typo means the real password property is never marked sensitive
user { 'alice':
  ensure    => present,
  password  => 'hunter2',
  sensitive => ['pasword'],   # -> the property itself is not defined on user
}

# after: correct name; also prefer wrapping the value in Sensitive() for redaction at the source
user { 'alice':
  ensure    => present,
  password  => Sensitive('hunter2'),
  sensitive => ['password'],
}
Defensive patterns

Strategy: type-guard

Validate before calling

# CI pre-flight: every sensitive=> name must be a real property of its type
manifests_with_sensitive.each do |type_name, names|
  bad = names.reject { |n| sensitive_property?(type_name, n) }
  fail "#{type_name}: sensitive names not defined as properties: #{bad.join(', ')}" unless bad.empty?
end

Type guard

def sensitive_property?(type_name, name)
  klass = Puppet::Type.type(type_name.to_sym)&.attrclass(name.to_sym)
  klass.is_a?(Class) && klass < Puppet::Property
end

sensitive_property?(:user, 'password')  # => true
sensitive_property?(:user, 'pasword')    # => false (would hit this err)

Prevention

When it happens

Trigger: sensitive => ['pasword'] (typo) on a user resource whose password property is set in clear text; listing an attribute that exists on another type (sensitive => ['content'] on package); referencing a property that was renamed or removed between module or Puppet versions; a name never registered via newproperty/newparam on a custom type.

Common situations: Hand-written manifests with spelling mistakes; copy-paste of sensitive lists between resource types; module upgrades that rename properties; custom types where the property is defined conditionally.

Related errors


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

Appendix: source

Thrown at lib/puppet/forge/repository.rb:56

        str += Puppet::Util.uri_encode(path)
        uri = URI(str)

        headers = { "User-Agent" => user_agent }

        if forge_authorization
          uri.user = nil
          uri.password = nil
          headers["Authorization"] = forge_authorization
        end

        http = Puppet.runtime[:http]
        response = http.get(uri, headers: headers, options: { ssl_context: @ssl_context })
        io.write(response.body) if io.respond_to?(:write)
        response
      rescue Puppet::SSL::CertVerifyError => e
        raise SSLVerifyError.new(:uri => @uri.to_s, :original => e.cause)
      rescue => e
        raise CommunicationError.new(:uri => @uri.to_s, :original => e)
      end
    end

    def forge_authorization
      if Puppet[:forge_authorization]
        Puppet[:forge_authorization]
      elsif Puppet.features.pe_license?
        PELicense.load_license_key.authorization_token
      end
    end

    # Return the local file name containing the data downloaded from the
    # repository at +release+ (e.g. "myuser-mymodule").
    def retrieve(release)
      path = @host.chomp('/') + release
      cache.retrieve(path)
    end

View on GitHub (pinned to e227c27540)