puppetlabs/puppet · warning · Puppet::Module::Task::InvalidMetadata

puppet.tasks/unreadable-metadata

puppet.tasks/unreadable-metadata

Error message

Error reading metadata: %{message}

What it means

Raised as Puppet::Error by Config#parse_directive when var is anything other than 'type', 'url', or 'debug'. In practice it is unreachable through Config#read because the preceding line regex only lets type|url|debug lines reach parse_directive; the branch is defensive code against future directives or direct calls. If you see it, something is calling parse_directive directly with an unknown key.

Source

Thrown at lib/puppet/module/task.rb:248

        raise InvalidName, _("Task names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores")
      end

      name = task_name == "init" ? pup_module.name : "#{pup_module.name}::#{task_name}"

      @module = pup_module
      @name = name
      @metadata_file = metadata_file
      @module_executables = module_executables || []
    end

    def self.read_metadata(file)
      if file
        content = Puppet::FileSystem.read(file, :encoding => 'utf-8')
        content.empty? ? {} : Puppet::Util::Json.load(content)
      end
    rescue SystemCallError, IOError => err
      msg = _("Error reading metadata: %{message}" % { message: err.message })
      raise InvalidMetadata.new(msg, 'puppet.tasks/unreadable-metadata')
    rescue Puppet::Util::Json::ParseError => err
      raise InvalidMetadata.new(err.message, 'puppet.tasks/unparseable-metadata')
    end

    def metadata
      @metadata ||= self.class.read_metadata(@metadata_file)
    end

    def files
      @files ||= self.class.find_files(@name, @module.tasks_directory, metadata, @module_executables, environment_name)
    end

    def validate
      files
      true
    end

    def ==(other)

View on GitHub (pinned to e227c27540)

Solutions

  1. If calling parse_directive directly, pass only 'type', 'url', or 'debug' as var
  2. If you extended the reader regex, extend parse_directive's case with your new key in the same patch
  3. Un-vendor/align Puppet versions so the regex and the case statement come from the same release

Example fix

# before
config.parse_directive(device, 'user', 'admin', 4) # raises Invalid argument 'user'

# after
config.parse_directive(device, 'url', 'ssh://admin@sw01.example.com', 4)
Defensive patterns

Strategy: type-guard

Type guard

SUPPORTED_DIRECTIVES = %w[type url debug].freeze

def supported_directive?(var)
  SUPPORTED_DIRECTIVES.include?(var)
end

config.parse_directive(device, var, value, line) if supported_directive?(var)

Try / catch

begin
  config.parse_directive(device, var, value, line)
rescue Puppet::Error => e
  raise unless e.message =~ /Invalid argument/
  Puppet.debug("ignoring unsupported device.conf directive #{var}")
end

Prevention

When it happens

Trigger: Custom code invoking Puppet::Util::NetworkDevice::Config#parse_directive(device, 'user', 'admin', line) or similar with an unsupported var; a patched/older read regex that forwards new directive names to an unpatched parse_directive after a partial upgrade.

Common situations: Monkey-patches or vendored forks that add directives to the regex but not to the case statement; version skew between a patched config reader and the stock parser.

Related errors


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