antiwork/gumroad · warning · GlobalAffiliates::ProductEligibilityController::InvalidUrl

Please provide a valid Gumroad product URL

Error message

Please provide a valid Gumroad product URL

What it means

First of five `InvalidUrl` raise sites in `fetch_and_parse_product_data` (global_affiliates/product_eligibility_controller.rb:28). The pasted URL parsed successfully, but `Addressable::URI#domain` is not one of `GUMROAD_DOMAINS` (built from ROOT_DOMAIN, SHORT_DOMAIN, and DOMAIN), so the URL is not on a Gumroad-owned host. The controller rescues `InvalidUrl`/`URI::InvalidURIError` and returns `{ success: false, error: "Please provide a valid Gumroad product URL" }`.

Source

Thrown at app/controllers/global_affiliates/product_eligibility_controller.rb:28

    product_data = fetch_and_parse_product_data
    render json: { success: true, product: product_data }
  rescue InvalidUrl, URI::InvalidURIError, Addressable::URI::InvalidURIError
    render json: { success: false, error: "Please provide a valid Gumroad product URL" }
  end

  private
    # Resolve the pasted URL to a product, then shape exactly the fields the
    # affiliate-eligibility UI needs. We fetch the product's own public JSON
    # endpoint (GET /l/:permalink.json) to leverage its URL routing — that
    # handles short domains, subdomains, and custom permalinks for free — but
    # we read `recommendable?` from the model directly. Recommendability is an
    # affiliate-program eligibility concept, not part of the public product API
    # surface (ProductPresenter::PublicApiProps), so it must not be exposed
    # there; resolving it locally keeps that boundary clean.
    def fetch_and_parse_product_data
      uri = Addressable::URI.parse(params[:url])
      raise InvalidUrl unless GUMROAD_DOMAINS.include?(uri&.domain)
      uri.path = uri.path + ".json"

      response = HTTParty.get(uri.to_s)
      raise InvalidUrl unless response.ok?

      data = response.to_hash
      raise InvalidUrl unless data["api_version"] == ProductPresenter::PublicApiProps::API_VERSION && data["permalink"].present?

      id = data["id"]
      raise InvalidUrl if id.blank?

      product = Link.find_by_external_id(id)
      raise InvalidUrl if product.nil?

      {
        "name" => product.name,
        "formatted_price" => product.price_formatted_verbose,
        "recommendable" => product.recommendable?,

View on GitHub (pinned to afeacbd394)

Solutions

  1. Paste a full Gumroad product URL including scheme and host: https://gumroad.com/l/<permalink>, https://<subdomain>.gumroad.com/l/<permalink>, or https://gum.co/<permalink>.
  2. Make sure the `url` param is non-empty and includes the protocol (https://) — bare permalinks or paths fail the domain check.
  3. Do not use third-party shorteners or non-Gumroad mirrors; resolve them to the final gumroad.com URL first.
  4. As a caller, pre-validate with `GUMROAD_DOMAINS.include?(Addressable::URI.parse(url)&.domain)` before submitting.

Example fix

# before
GlobalAffiliates::ProductEligibilityController show with params: { url: "https://example.com/l/demo" } # => error

# after
uri = Addressable::URI.parse("https://gumroad.com/l/demo")
GlobalAffiliates eligibility check with params: { url: uri.to_s } if ["gumroad.com", "gum.co"].include?(uri.domain)
Defensive patterns

Strategy: validation

Validate before calling

uri = Addressable::URI.parse(input_url)
allowed = ["gumroad.com", "gum.co"].include?(uri&.domain) && uri.path.present?
submit_eligibility_check(url: uri.to_s) if allowed

Type guard

def gumroad_product_url?(value)
  uri = Addressable::URI.parse(value.to_s)
  GUMROAD_DOMAINS.include?(uri&.domain) && uri.path.to_s.match?(%r{\A/(l/)?[^/]+})
rescue Addressable::URI::InvalidURIError
  false
end

Try / catch

def show
  render json: { success: true, product: fetch_and_parse_product_data }
rescue InvalidUrl, URI::InvalidURIError, Addressable::URI::InvalidURIError
  render json: { success: false, error: "Please provide a valid Gumroad product URL" }
end

Prevention

When it happens

Trigger: GET global_affiliates product eligibility check with `url` param pointing at a non-Gumroad host (e.g. https://example.com/l/foo), a bare path with no host ("/l/foo"), a third-party link shortener, or a seller's custom non-Gumroad domain. Also fires when the param is missing so `uri&.domain` is nil.

Common situations: Users paste a Shopify/Lemon Squeezy/own-domain checkout link instead of the Gumroad product URL; the field is left empty or contains just a permalink; someone expects arbitrary redirects to be followed.

Related errors


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