puppetlabs/puppet · error · Puppet::Module::Task::InvalidTask

puppet.tasks/missing-implementation

puppet.tasks/missing-implementation

Error message

Task metadata for task %{name} specifies missing implementation %{implementation}

What it means

Raised as Puppet::Error while parsing device.conf ($confdir/device.conf) when a second `[device.name]` section header appears for a name already registered. The parser tracks each stanza's starting line, so the message points at both the duplicate's line and the first occurrence's line. Reading happens for `puppet device` runs; a duplicate makes the configuration ambiguous, so parsing aborts.

Source

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

      basename = name.split('::')[1] || 'init'
      # If 'implementations' is defined, it needs to mention at least one
      # implementation, and everything it mentions must exist.
      metadata ||= {}
      if metadata.key?('implementations')
        unless metadata['implementations'].is_a?(Array)
          msg = _("Task metadata for task %{name} does not specify implementations as an array" % { name: name })
          raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')
        end

        implementations = metadata['implementations'].map do |impl|
          unless impl['requirements'].is_a?(Array) || impl['requirements'].nil?
            msg = _("Task metadata for task %{name} does not specify requirements as an array" % { name: name })
            raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')
          end
          path = executables.find { |real_impl| File.basename(real_impl) == impl['name'] }
          unless path
            msg = _("Task metadata for task %{name} specifies missing implementation %{implementation}" % { name: name, implementation: impl['name'] })
            raise InvalidTask.new(msg, 'puppet.tasks/missing-implementation', { missing: [impl['name']] })
          end
          { "name" => impl['name'], "path" => path }
        end
        return implementations
      end

      # If implementations isn't defined, then we use executables matching the
      # task name, and only one may exist.
      implementations = executables.select { |impl| File.basename(impl, '.*') == basename }
      if implementations.empty?
        msg = _('No source besides task metadata was found in directory %{directory} for task %{name}') %
              { name: name, directory: directory }
        raise InvalidTask.new(msg, 'puppet.tasks/no-implementation')
      elsif implementations.length > 1
        msg = _("Multiple executables were found in directory %{directory} for task %{name}; define 'implementations' in metadata to differentiate between them") %
              { name: name, directory: implementations[0] }
        raise InvalidTask.new(msg, 'puppet.tasks/multiple-implementations')
      end

View on GitHub (pinned to e227c27540)

Solutions

  1. Open device.conf at the line in %{file_error_location} and delete the duplicate stanza (the first is at %{device_error_location})
  2. If device.conf is assembled from fragments, deduplicate the source of the fragment that emits the device twice
  3. Validate before deploying: `awk '/^\[/{print}' device.conf | sort | uniq -d` must be empty

Example fix

# before (/etc/puppetlabs/puppet/device.conf)
[sw01.example.com]
type cisco
url ssh://admin:pass@sw01.example.com
[sw01.example.com]
type cisco
url ssh://admin:pass@sw01.example.com

# after
[sw01.example.com]
type cisco
url ssh://admin:pass@sw01.example.com
Defensive patterns

Strategy: validation

Validate before calling

names = File.read('/etc/puppetlabs/puppet/device.conf').scan(/^\[([\w.-]+)\]/).flatten
dups = names.tally.select { |_, c| c > 1 }.keys
raise "duplicate devices in device.conf: #{dups.join(', ')}" unless dups.empty?

Try / catch

begin
  config.read
rescue Puppet::Error => e
  raise unless e.message =~ /Duplicate device/
  # message names both line numbers; fix file and re-read
  raise
end

Prevention

When it happens

Trigger: Editing device.conf and copy-pasting a stanza, leaving two [sw01.example.com] headers; merges or templating that append a stanza without removing the original; accidental duplication when managing device.conf with concat/file_line fragments.

Common situations: device.conf managed by multiple fragments or by both a template and a manual edit; renaming devices and forgetting to delete the old stanza; version-control merge conflicts resolved by keeping both blocks.

Related errors


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