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')
endView on GitHub (pinned to e227c27540)
Solutions
- Open device.conf at the line in %{file_error_location} and delete the duplicate stanza (the first is at %{device_error_location})
- If device.conf is assembled from fragments, deduplicate the source of the fragment that emits the device twice
- 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
- Generate device.conf from a single source of truth (template over your inventory) instead of fragment edits
- Add a uniq -d header check to config deployment pipelines
- Resolve VCS merge conflicts on device.conf by inspection, never keep both sides
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
- puppet.tasks/no-implementation
- puppet.tasks/multiple-implementations
- puppet.tasks/unreadable-metadata
- Can't parse line '%{line}'
- Section %{name} is already defined, cannot redefine
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/f3801fc3041a33c4.
Report an issue: GitHub.