{"record":{"id":"6dcb00c4c7f0194c","repo":"jordansissel/fpm","slug":"metacpan-query-failed","errorCode":null,"errorMessage":"metacpan query failed","messagePattern":"metacpan query failed","errorType":"validation","errorClass":"FPM::InvalidPackageConfiguration","httpStatus":null,"severity":"error","filePath":"lib/fpm/package/cpan.rb","lineNumber":364,"sourceCode":"    release_metadata = JSON.parse(data)\n\n    download_url = release_metadata['hits']['hits'][0]['_source']['download_url']\n    download_path = URI.parse(download_url).path\n    tarball = File.basename(download_path)\n\n    url_base = \"http://www.cpan.org/\"\n    url_base = \"#{attributes[:cpan_mirror]}\" if !attributes[:cpan_mirror].nil?\n\n    url = \"#{url_base}#{download_path}\"\n    logger.debug(\"Fetching perl module\", :url => url)\n\n    begin\n      response = httpfetch(url)\n    rescue Net::HTTPServerException => e\n      #logger.error(\"Download failed\", :error => response.status_line,\n                    #:url => url)\n      logger.error(\"Download failed\", :error => e, :url => url)\n      raise FPM::InvalidPackageConfiguration, \"metacpan query failed\"\n    end\n\n    File.open(build_path(tarball), \"w\") do |fd|\n      #response.read_body { |c| fd.write(c) }\n      fd.write(response.body)\n    end\n    return build_path(tarball)\n  end # def download\n\n  def search_module(module_name, version=nil)\n    logger.info(\"Asking metacpan about a module\", :module => module_name, :version => version)\n    metacpan_api_url = \"#{attributes[:cpan_metacpan_api_url]}/v1/module/_search\"\n    metacpan_api_query = <<-EOS\n{\n  \"query\": {\n    \"bool\": {\n      \"must\": [\n        { \"term\": { \"module.name\": \"#{module_name}\" } },","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/jordansissel/fpm/blob/b6d77ba72a560b687723376a0e5115c3a92634ad/lib/fpm/package/cpan.rb#L346-L382","documentation":"Despite the message saying 'metacpan query failed', this error is raised when downloading the tarball fails: fpm has already obtained download_path from metacpan and then fetches url = <cpan_mirror><download_path> via httpfetch. A Net::HTTPServerException (4xx, typically 404 from the mirror) triggers FPM::InvalidPackageConfiguration with this misleading text.","triggerScenarios":"fpm -s cpan fetches e.g. http://www.cpan.org/authors/id/.../Foo-1.00.tar.gz and the mirror returns 404: the mirror is out of sync or incomplete (common with small --cpan-mirror values like a minicpan), the release was just uploaded and has not propagated, or the resolved version does not exist as a file on that mirror.","commonSituations":"Using --cpan-mirror pointing at a partial/minicpan mirror that lacks authors/ paths; a custom mirror with a different URL layout (missing trailing content); packaging a freshly indexed release before mirrors sync; corporate proxy returning 403 on the CPAN host.","solutions":["Take the logged url value and curl -I it to see the exact status from your mirror","Switch to a complete mirror: --cpan-mirror http://www.cpan.org/ (default) or another full mirror from http://mirrors.cpan.org/","Retry later if the release was just uploaded to CPAN (mirror propagation lag)","Drop the explicit --version so metacpan resolves a release that definitely exists on mirrors"],"exampleFix":"# before\nfpm -s cpan -t deb Foo --cpan-mirror http://minicpan.local   # 404 -> 'metacpan query failed'\n\n# after\nfpm -s cpan -t deb Foo --cpan-mirror http://www.cpan.org/","handlingStrategy":"retry","validationCode":"require 'net/http'\n\n# HEAD-check the exact tarball URL fpm will fetch from your mirror\nurl = URI.join(mirror_url, download_path)   # e.g. http://www.cpan.org/authors/id/.../Foo-1.00.tar.gz\ncode = Net::HTTP.start(url.host, url.port) { |http| http.head(url.request_uri).code }\nabort \"mirror says HTTP #{code} for #{url}\" unless code == '200'","typeGuard":null,"tryCatchPattern":"mirrors = ['http://www.cpan.org/', 'http://cpan.cpantesters.org/']\nbegin\n  pkg.input('Foo')   # uses attributes[:cpan_mirror]\nrescue FPM::InvalidPackageConfiguration => e\n  raise unless e.message.include?('metacpan query failed')  # misleading name: this is the mirror download\n  raise if mirrors.empty?\n  pkg.attributes[:cpan_mirror] = mirrors.shift\n  retry\nend","preventionTips":["Use a full CPAN mirror, not a minicpan, when building with -s cpan","For freshly released modules, wait for mirror propagation before packaging","HEAD-check the download URL in a preflight step so failures are attributed to the mirror, not metacpan"],"tags":["fpm","cpan","download","mirror","http-404","packaging"],"backgroundTag":"artifact-download-404","analyzedSha":"b6d77ba72a560b687723376a0e5115c3a92634ad","analyzedAt":"2026-08-21T16:39:02.570Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}