antiwork/gumroad · error · CreatePublicMediaService::RemoteFileTooLarge

That file is too large. Images can be up to #{MAX_IMAGE_BYTE

Error message

That file is too large. Images can be up to #{MAX_IMAGE_BYTES / 1.megabyte} MB.

What it means

Before streaming any body bytes, download_blob_from_url compares the remote response's Content-Length header against MAX_IMAGE_BYTES (10 MB) and raises RemoteFileTooLarge; #process rescues it into this message. This is the fast path of the size ceiling: an oversized file is cut off before it is downloaded, instead of being fully fetched and uploaded to public storage only to be rejected afterwards.

Source

Thrown at app/services/create_public_media_service.rb:189

      blob
    end

    # Download the remote file to a tempfile with SSRF protection and a hard size ceiling, then
    # store it as a blob. The size is enforced while streaming (both via the Content-Length header
    # and by counting actual bytes) so a file larger than the 10 MB image cap is cut off
    # mid-download instead of being fully downloaded (and uploaded to public storage) only to be
    # rejected by the size check afterwards. Mirrors the hardened fetch the product thumbnail's
    # URL path already uses (Thumbnail#url=).
    def download_blob_from_url
      normalized_url = normalize_url(url)
      uri = URI.parse(normalized_url)
      raise URI::InvalidURIError, "URL '#{normalized_url}' is not a web url" unless uri.scheme.in?(%w[http https])
      raise URI::InvalidURIError, "URL must include a valid host" if uri.host.blank?

      tempfile = Tempfile.new(binmode: true)
      begin
        response = SsrfFilter.get(normalized_url) do |http_response|
          raise RemoteFileTooLarge if http_response["content-length"].to_i > MAX_IMAGE_BYTES

          write_file = http_response.is_a?(Net::HTTPSuccess)
          received_bytes = 0
          byte_limit = MAX_IMAGE_BYTES
          http_response.read_body do |chunk|
            received_bytes += chunk.bytesize
            raise RemoteFileTooLarge if received_bytes > byte_limit

            tempfile.write(chunk) if write_file
          end
        end
        raise ActiveStorage::FileNotFoundError unless response.is_a?(Net::HTTPSuccess)

        tempfile.rewind
        # Sniff the real content type from the file bytes. The remote server's header is used only
        # as a hint — a mislabeled or disguised file is classified by what it actually contains.
        content_type = Marcel::MimeType.for(tempfile, name: filename_from(uri), declared_type: response.content_type)
        tempfile.rewind

View on GitHub (pinned to afeacbd394)

Solutions

  1. Host and submit a web-sized version of the image (resize/compress until under 10 MB)
  2. Use a CDN thumbnail parameter if available, e.g. '?w=2000' or '/_2000x/...' variants
  3. Compress locally (e.g. 'convert in.png -resize 2000x -quality 85 out.jpg') and link that file
  4. Or direct-upload the compressed file and pass signed_blob_id instead of url

Example fix

# before
url: 'https://cdn.example.com/brand-poster.png' # Content-Length: 32 MB
# => failure: That file is too large. Images can be up to 10 MB.

# after
url: 'https://cdn.example.com/brand-poster.png?w=2000&fm=jpg' # ~1 MB
Defensive patterns

Strategy: validation

Validate before calling

MAX_IMAGE_BYTES = 10.megabytes

# hint only: dynamic endpoints can lie or omit the header
size_hint = URI.open(url, 'r') { |f| f.meta['content-length']&.to_i }
return if size_hint && size_hint <= MAX_IMAGE_BYTES
raise 'file likely exceeds the 10 MB cap' if size_hint

Prevention

When it happens

Trigger: Passing url pointing at a file whose server advertises Content-Length greater than 10_485_760 bytes (e.g. a 32 MB PNG or a print-resolution export) to CreatePublicMediaService with only url set (no signed_blob_id).

Common situations: Sellers link high-resolution photos or unoptimized design-tool exports hosted on their own site or CDN; images large enough for print are routinely over 10 MB.

Related errors


AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21). Data as JSON: /api/errors/6ef7a67105868eab. Report an issue: GitHub.