{"record":{"id":"02fa5514b0121bc2","repo":"puppetlabs/puppet","slug":"cannot-lookup-multiple-files","errorCode":null,"errorMessage":"cannot lookup multiple files","messagePattern":"cannot lookup multiple files","errorType":"exception","errorClass":"Puppet::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/indirector/file_metadata/http.rb","lineNumber":34,"sourceCode":"    uri = URI(request.uri)\n    client = Puppet.runtime[:http]\n    head = client.head(uri, options: { include_system_store: true })\n\n    return create_httpmetadata(head, checksum_type) if head.success?\n\n    case head.code\n    when 403, 405\n      # AMZ presigned URL and puppetserver may return 403\n      # instead of 405. Fallback to partial get\n      get = partial_get(client, uri)\n      return create_httpmetadata(get, checksum_type) if get.success?\n    end\n\n    nil\n  end\n\n  def search(request)\n    raise Puppet::Error, _(\"cannot lookup multiple files\")\n  end\n\n  private\n\n  def partial_get(client, uri)\n    client.get(uri, headers: { 'Range' => 'bytes=0-0' }, options: { include_system_store: true })\n  end\n\n  def create_httpmetadata(http_request, checksum_type)\n    metadata = Puppet::FileServing::HttpMetadata.new(http_request)\n    metadata.checksum_type = checksum_type if checksum_type\n    metadata.collect\n    metadata\n  end\nend\n","sourceCodeStart":16,"sourceCodeEnd":50,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/indirector/file_metadata/http.rb#L16-L50","documentation":"The http file_metadata terminus serves metadata for a single HTTP(S) URL via HEAD, falling back to a one-byte Range GET when the server answers 403/405 (e.g. S3 presigned URLs). Puppet's search action means metadata for many files / recursive traversal, which plain web servers cannot express, so search unconditionally raises Puppet::Error 'cannot lookup multiple files'.","triggerScenarios":"A file (or similar) resource whose source is 'http://' or 'https://' combined with recurse => true or sources => containing multiple http URLs; a Ruby call to Puppet::FileServing::Metadata.indirection.search with an http(s) URI.","commonSituations":"Porting a recursive copy from puppet:// to a web server or object store; pointing file resources at artifact directories served by nginx/S3; expecting directory listing semantics from presigned URLs.","solutions":["Point each resource at a single file URL and generate one resource per file (e.g. from a manifest loop or a generated list).","Remove recurse / recurse_remote from resources whose source scheme is http or https.","If recursion is required, sync the remote tree into a module or another puppet-served location and use a puppet:/// source."],"exampleFix":"# before\nfile { '/opt/app':\n  ensure  => directory,\n  recurse => true,\n  source  => 'https://repo.example.com/app/1.2.3/',\n}\n# Error: cannot lookup multiple files\n\n# after: stage the tree in a module, recurse over puppet://\nfile { '/opt/app':\n  ensure  => directory,\n  recurse => true,\n  source  => 'puppet:///modules/app_files/1.2.3',\n}","handlingStrategy":"validation","validationCode":"def http_source?(uri)\n  %w[http https].include?(URI.parse(uri).scheme.to_s)\nend\n\nraise ArgumentError, 'http(s) sources cannot be recursive' if http_source?(source) && opts[:recurse]","typeGuard":"def single_http_file?(uri)\n  u = URI.parse(uri.to_s)\n  %w[http https].include?(u.scheme.to_s) && !u.path.to_s.end_with?('/')\nend","tryCatchPattern":null,"preventionTips":["Treat http(s) file sources as single-file only by design.","Review any resource mixing recurse with a non-puppet scheme.","Serve trees from modules or fileserver mounts when recursion is needed."],"tags":["puppet","file-metadata","http-source","recursion","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}