{"record":{"id":"2bc9d25f497adc60","repo":"puppetlabs/puppet","slug":"puppet-task-invalid-metadata","errorCode":"puppet.task/invalid-metadata","errorMessage":"Files specified in task metadata cannot include a trailing slash: %{file}","messagePattern":"Files specified in task metadata cannot include a trailing slash: %(.+?)","errorType":"validation","errorClass":"Puppet::Module::Task::InvalidMetadata","httpStatus":null,"severity":"error","filePath":"lib/puppet/module/task.rb","lineNumber":147,"sourceCode":"        end\n\n        unless File.exist?(path)\n          msg = _(\"Could not find %{path} on disk\" % { path: path })\n          raise InvalidFile, msg\n        end\n\n        last_char = file[-1] == '/'\n        if File.directory?(path)\n          unless last_char\n            msg = _(\"Directories specified in task metadata must include a trailing slash: %{dir}\" % { dir: file })\n            raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n          end\n          dir_files = Dir.glob(\"#{path}**/*\").select { |f| File.file?(f) }\n          dir_files.map { |f| get_file_details(f, pup_module) }\n        else\n          if last_char\n            msg = _(\"Files specified in task metadata cannot include a trailing slash: %{file}\" % { file: file })\n            raise InvalidMetadata.new(msg, 'puppet.task/invalid-metadata')\n          end\n          get_file_details(path, pup_module)\n        end\n      end\n    end\n    private_class_method :find_extra_files\n\n    # Executables list should contain the full path of all possible implementation files\n    def self.find_implementations(name, directory, metadata, executables)\n      basename = name.split('::')[1] || 'init'\n      # If 'implementations' is defined, it needs to mention at least one\n      # implementation, and everything it mentions must exist.\n      metadata ||= {}\n      if metadata.key?('implementations')\n        unless metadata['implementations'].is_a?(Array)\n          msg = _(\"Task metadata for task %{name} does not specify implementations as an array\" % { name: name })\n          raise InvalidMetadata.new(msg, 'puppet.tasks/invalid-metadata')\n        end","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/module/task.rb#L129-L165","documentation":"Raised as ArgumentError by Puppet::Util::Ldap::Manager#entry2provider when the entry hash passed in has no 'dn' key. The conversion immediately needs the distinguished name to derive the resource name (it pops the single dn value and strips it down to the rdn value), so an entry without dn cannot be converted at all. This is a contract violation between the LDAP search layer and the manager, not an I/O failure.","triggerScenarios":"Calling manager.entry2provider(entry) with a hash built by hand or by a search whose attribute list excludes dn; tests with fixture entries that omit 'dn'; custom code reusing the manager on entries obtained from a different LDAP client that returns dn separately from attributes.","commonSituations":"rspec fixtures copied from ldapsearch output without the dn: line; code paths that filter search results and accidentally drop the dn pair; upgrades of client libraries that stop merging dn into the attribute hash.","solutions":["Ensure the search requests the dn (or that the client keeps it in the attribute hash) before calling entry2provider","Fix fixture/spec hashes to include 'dn' => ['cn=name,ou=...,dc=...']","Guard at the call site: skip or log entries lacking 'dn' instead of passing them through"],"exampleFix":"# before\nentry = { 'cn' => ['web01'], 'objectclass' => ['top'] }\nmanager.entry2provider(entry) # raises ArgumentError\n\n# after\nentry = { 'dn' => ['cn=web01,ou=hosts,dc=example,dc=com'], 'cn' => ['web01'], 'objectclass' => ['top'] }\nmanager.entry2provider(entry)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"def convertible_entry?(entry)\n  entry.is_a?(Hash) && entry['dn'].is_a?(Array) && !entry['dn'].empty?\nend\n\nresults.select { |e| convertible_entry?(e) }.each { |e| manager.entry2provider(e) }","tryCatchPattern":"begin\n  manager.entry2provider(entry)\nrescue ArgumentError => e\n  raise unless e.message =~ /Could not get dn/\n  Puppet.debug(\"skipping LDAP entry without dn: #{entry.inspect}\")\nend","preventionTips":["Always request/keep dn in LDAP search attribute lists","Include 'dn' in every rspec fixture entry used with the manager","Skip-and-log entries failing the guard instead of letting one bad entry abort the batch"],"tags":["puppet","ldap","entry-conversion","missing-key"],"backgroundTag":"missing-required-field","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}