{"record":{"id":"dc5b69adf25e8240","repo":"antiwork/gumroad","slug":"we-couldn-t-download-that-file-please-check-the-u","errorCode":null,"errorMessage":"We couldn't download that file, please check the URL and try again.","messagePattern":"We couldn't download that file, please check the URL and try again\\.","errorType":"exception","errorClass":"ActiveStorage::FileNotFoundError","httpStatus":null,"severity":"error","filePath":"app/services/create_public_media_service.rb","lineNumber":201,"sourceCode":"      raise URI::InvalidURIError, \"URL '#{normalized_url}' is not a web url\" unless uri.scheme.in?(%w[http https])\n      raise URI::InvalidURIError, \"URL must include a valid host\" if uri.host.blank?\n\n      tempfile = Tempfile.new(binmode: true)\n      begin\n        response = SsrfFilter.get(normalized_url) do |http_response|\n          raise RemoteFileTooLarge if http_response[\"content-length\"].to_i > MAX_IMAGE_BYTES\n\n          write_file = http_response.is_a?(Net::HTTPSuccess)\n          received_bytes = 0\n          byte_limit = MAX_IMAGE_BYTES\n          http_response.read_body do |chunk|\n            received_bytes += chunk.bytesize\n            raise RemoteFileTooLarge if received_bytes > byte_limit\n\n            tempfile.write(chunk) if write_file\n          end\n        end\n        raise ActiveStorage::FileNotFoundError unless response.is_a?(Net::HTTPSuccess)\n\n        tempfile.rewind\n        # Sniff the real content type from the file bytes. The remote server's header is used only\n        # as a hint — a mislabeled or disguised file is classified by what it actually contains.\n        content_type = Marcel::MimeType.for(tempfile, name: filename_from(uri), declared_type: response.content_type)\n        tempfile.rewind\n        ActiveStorage::Blob.create_and_upload!(\n          io: tempfile,\n          filename: filename_with_extension(filename_from(uri), content_type),\n          content_type:,\n        )\n      ensure\n        tempfile.close!\n      end\n    end\n\n    def normalize_url(raw)\n      value = raw.to_s","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/services/create_public_media_service.rb#L183-L219","documentation":"Raised as ActiveStorage::FileNotFoundError at line 201 when SsrfFilter.get completes but the response is not a Net::HTTPSuccess, and mapped to this message; #process's rescue also maps INTERNET_EXCEPTIONS (SocketError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH, ...) to the same string. It means the URL was structurally valid and SSRF-safe but the fetch itself failed: a 4xx/5xx status or a transport-level error. (Note: an unresolvable hostname maps to the 'valid public URL' message instead, via SsrfFilter::UnresolvedHostname.)","triggerScenarios":"The remote server returns 404 (file moved/deleted), 403 (hotlink protection or an expired presigned URL), or 5xx; or the TCP connection is refused/reset mid-fetch. URL passed all scheme/host/SSRF checks, so the failure is downstream of them.","commonSituations":"Expired S3/Google-Drive presigned links, hosts that block non-browser user-agents or referers, origin outages behind a CDN, transient 502/503s.","solutions":["Open the URL in an incognito window to confirm it is publicly reachable without cookies","Use a stable permalink (your own hosting) rather than expiring share links","Retry after a short wait if the failure looked like a transient 5xx or connection reset","Direct-upload the file (signed_blob_id) so no remote fetch is needed at all"],"exampleFix":"# before\nurl: 'https://s3.amazonaws.com/bucket/logo?X-Amz-Expires=60' # link expired -> 403\n# => failure: We couldn't download that file...\n\n# after\nurl: 'https://seller-site.com/assets/logo.png' # permanent, public","handlingStrategy":"retry","validationCode":"require 'net/http'\n\nuri = URI(url)\nNet::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', open_timeout: 5) do |http|\n  code = http.head(uri.request_uri).code\n  raise \"endpoint returns #{code}\" unless code.start_with?('2')\nend","typeGuard":null,"tryCatchPattern":"result = nil\nbegin\n  attempts = (attempts || 0) + 1\n  result = CreatePublicMediaService.new(seller:, url:).process\n  # HTTP 5xx / connection resets land here as failure Result, not exceptions:\n  # retry the whole process call with backoff while attempts < 3\nend while !result.success? && result.error_message.include?(%q[couldn't download]) && attempts < 3","preventionTips":["Pre-flight the URL with a HEAD request from the same environment before submitting it","Host files at stable public permalinks rather than expiring presigned URLs or hotlink-protected origins","Distinguish failure modes by message only as a last resort — better to fix the URL than to classify the error string"],"tags":["ruby","rails","http-download","network","active-storage"],"backgroundTag":"url-download-failed","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}