{"record":{"id":"7d41346189d0947f","repo":"puppetlabs/puppet","slug":"puppet-tasks-no-implementation","errorCode":"puppet.tasks/no-implementation","errorMessage":"No source besides task metadata was found in directory %{directory} for task %{name}","messagePattern":"No source besides task metadata was found in directory %(.+?) for task %(.+?)","errorType":"validation","errorClass":"Puppet::Module::Task::InvalidTask","httpStatus":null,"severity":"error","filePath":"lib/puppet/module/task.rb","lineNumber":188,"sourceCode":"            raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n          end\n          path = executables.find { |real_impl| File.basename(real_impl) == impl['name'] }\n          unless path\n            msg = _(\"Task metadata for task %{name} specifies missing implementation %{implementation}\" % { name: name, implementation: impl['name'] })\n            raise InvalidTask.new(msg, 'puppet.tasks/missing-implementation', { missing: [impl['name']] })\n          end\n          { \"name\" => impl['name'], \"path\" => path }\n        end\n        return implementations\n      end\n\n      # If implementations isn't defined, then we use executables matching the\n      # task name, and only one may exist.\n      implementations = executables.select { |impl| File.basename(impl, '.*') == basename }\n      if implementations.empty?\n        msg = _('No source besides task metadata was found in directory %{directory} for task %{name}') %\n              { name: name, directory: directory }\n        raise InvalidTask.new(msg, 'puppet.tasks/no-implementation')\n      elsif implementations.length > 1\n        msg = _(\"Multiple executables were found in directory %{directory} for task %{name}; define 'implementations' in metadata to differentiate between them\") %\n              { name: name, directory: implementations[0] }\n        raise InvalidTask.new(msg, 'puppet.tasks/multiple-implementations')\n      end\n\n      [{ \"name\" => File.basename(implementations.first), \"path\" => implementations.first }]\n    end\n    private_class_method :find_implementations\n\n    def self.find_files(name, directory, metadata, executables, envname = nil)\n      # PXP agent relies on 'impls' (which is the task file) being first if there is no metadata\n      find_implementations(name, directory, metadata, executables) + find_extra_files(metadata, envname)\n    end\n\n    def self.is_tasks_metadata_filename?(name)\n      is_tasks_filename?(name) && name.end_with?('.json')\n    end","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/module/task.rb#L170-L206","documentation":"Raised as Puppet::Error when a device.conf line matches none of the accepted forms: comment, blank, `[device.name]` header, or a `type|url|debug <value>` directive. The offending line's text is included verbatim in %{file_text} with its location, so the message is self-describing. It fires during config parsing for `puppet device`.","triggerScenarios":"A stray line in device.conf: a misspelled directive (`username = ...`, `timeout 30`), a value-only line, a continuation/indent leftover, section headers with invalid characters (the header regex requires [\\w.-]+), or Windows line endings making otherwise-valid lines unmatched.","commonSituations":"Hand edits adding unsupported keys; templating that injects comments without '#'; CRLF from editing on Windows; examples copied from docs that use unsupported directives like `user`.","solutions":["Go to the line named in %{error_location} and either fix it to `type|url|debug <value>` form, comment it with #, or delete it","Ensure section headers only contain word/dot/dash characters: [router-01.example.com]","Normalize line endings to LF if the file was edited on Windows","Re-run `puppet device --verbose` after each fix to confirm parsing passes"],"exampleFix":"# before\n[sw01.example.com]\ntype cisco\nurl ssh://admin:pass@sw01.example.com\nuser admin            # unsupported directive -> Invalid entry\n\n# after\n[sw01.example.com]\ntype cisco\nurl ssh://admin:pass@sw01.example.com\n# credentials belong in the url, not a 'user' line","handlingStrategy":"validation","validationCode":"VALID = /^\\s*(#.*|\\[\\w[\\w.-]*\\]\\s*|(type|url|debug)\\s+.+\\s*)$/\nFile.readlines('device.conf', chomp: true).each_with_index do |l, i|\n  raise \"bad device.conf line #{i + 1}: #{l}\" unless l =~ VALID\nend","typeGuard":null,"tryCatchPattern":"begin\n  config.read\nrescue Puppet::Error => e\n  raise unless e.message =~ /Invalid entry/\n  # %{file_text} shows the exact offending line; fix it and retry the device run\n  raise\nend","preventionTips":["Manage device.conf exclusively through Puppet templates, not hand edits","Use only the three supported directives: type, url, debug","Normalize to LF line endings when the file crosses platforms"],"tags":["puppet","network-device","device-conf","config-parsing"],"backgroundTag":"config-parse-error","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}