puppetlabs/puppet · error · ArgumentError

Paths to keyfiles must be absolute, not %{entry}

Error message

Paths to keyfiles must be absolute, not %{entry}

What it means

Raised by the validate block of the `purge_ssh_keys` property on the `user` type for each string entry that is neither an absolute path (Puppet::Util.absolute_path?, platform-aware) nor prefixed with ~/ or %h/. Puppet generates ssh_authorized_key purge resources from these paths and refuses relative ones because purging by a relative path would resolve against the agent's cwd unpredictably. %h expands to the user's home directory at munge time.

Source

Thrown at lib/puppet/type/user.rb:746

          the user's home directory."

      defaultto :false

      # Use Symbols instead of booleans until PUP-1967 is resolved.
      newvalues(:true, :false)

      validate do |value|
        if [:true, :false].include? value.to_s.intern
          return
        end

        value = [value] if value.is_a?(String)
        if value.is_a?(Array)
          value.each do |entry|
            raise ArgumentError, _("Each entry for purge_ssh_keys must be a string, not a %{klass}") % { klass: entry.class } unless entry.is_a?(String)

            valid_home = Puppet::Util.absolute_path?(entry) || entry =~ %r{^~/|^%h/}
            raise ArgumentError, _("Paths to keyfiles must be absolute, not %{entry}") % { entry: entry } unless valid_home
          end
          return
        end
        raise ArgumentError, _("purge_ssh_keys must be true, false, or an array of file names, not %{value}") % { value: value.inspect }
      end

      munge do |value|
        # Resolve string, boolean and symbol forms of true and false to a
        # single representation.
        case value
        when :false, false, "false"
          []
        when :true, true, "true"
          home = homedir
          home ? ["#{home}/.ssh/authorized_keys"] : []
        else
          # value can be a string or array - munge each value
          [value].flatten.filter_map do |entry|

View on GitHub (pinned to e227c27540)

Solutions

  1. Use an absolute path: `purge_ssh_keys => ['/etc/ssh/keys/alice/authorized_keys']`
  2. Or a home-relative prefix: `=> ['~/.ssh/authorized_keys']` or `=> ['%h/.ssh/authorized_keys']`
  3. Prefix generated paths explicitly: "${home}/.ssh/authorized_keys"

Example fix

# before
user { 'alice':
  ensure          => present,
  purge_ssh_keys  => ['.ssh/id_rsa'],
}

# after
user { 'alice':
  ensure          => present,
  purge_ssh_keys  => ['~/.ssh/authorized_keys', '%h/.ssh/id_rsa'],
}
Defensive patterns

Strategy: validation

Validate before calling

require 'puppet/util'
valid = keys.all? { |k| Puppet::Util.absolute_path?(k) || k.start_with?('~/', '%h/') }
raise ArgumentError, "purge_ssh_keys paths must be absolute or ~/ or %h/ prefixed: #{keys.inspect}" unless valid

Type guard

def absolute_or_home_prefixed?(entry)
  entry.is_a?(String) && (Puppet::Util.absolute_path?(entry) || entry =~ %r{\A(~|%h)/})
end

Prevention

When it happens

Trigger: `purge_ssh_keys => ['.ssh/authorized_keys']` or `=> ['keys/alice']`; Windows paths like 'C:Users\\x' missing a slash; entries built by joining without a leading slash.

Common situations: Shortening paths copied from ~/.ssh documentation; generating keys paths from variables that may be empty (producing '.ssh/...'); forgetting that home-relative entries must start with ~/ or %h/ exactly.

Related errors


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