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
- 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>.
- Make sure the `url` param is non-empty and includes the protocol (https://) — bare permalinks or paths fail the domain check.
- Do not use third-party shorteners or non-Gumroad mirrors; resolve them to the final gumroad.com URL first.
- 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
- Validate scheme + Gumroad domain client-side before submitting the form.
- Autofill the field from the product's own long_url when possible instead of pasting.
- Reject empty input early with a clear message.
- Don't feed arbitrary user URLs in — this endpoint intentionally refuses non-Gumroad hosts.
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.