antiwork/gumroad · error · Link::LinkInvalid
Offer code must apply to this product
Error message
Offer code must apply to this product
What it means
links_controller.rb:1933 raises `Link::LinkInvalid` when the default offer code fails `OfferCode#applicable?(@product)` (app/models/offer_code.rb:305): a non-universal code must list the product, a universal code must not exclude it, and a currency-scoped universal code must match the product's price currency. Because this controller lacks the unchanged-id early return, even re-sending the CURRENT id fails once applicability drifted — e.g. the product's currency changed so it no longer matches the code.
Source
Thrown at app/controllers/links_controller.rb:1933
end
@product.installment_plan&.destroy_if_no_payment_options!
@product.reset_installment_plan
if product_permitted_params[:installment_plan].present?
@product.create_installment_plan!(product_permitted_params[:installment_plan])
end
end
def update_default_offer_code
default_offer_code_id = product_permitted_params[:default_offer_code_id]
return @product.default_offer_code = nil if default_offer_code_id.blank?
offer_code = @product.user.offer_codes.alive.find_by_external_id!(default_offer_code_id)
raise Link::LinkInvalid, "Offer code cannot be expired" if offer_code.inactive?
raise Link::LinkInvalid, "Offer code must apply to this product" unless offer_code.applicable?(@product)
@product.default_offer_code = offer_code
rescue ActiveRecord::RecordNotFound
raise Link::LinkInvalid, "Invalid offer code"
end
def toggle_community_chat!(enabled)
return if [Link::NATIVE_TYPE_COFFEE, Link::NATIVE_TYPE_BUNDLE].include?(@product.native_type)
@product.toggle_community_chat!(enabled)
end
def generate_product_details_using_ai
if Rails.env.test?
generate_product_cover_and_thumbnail_using_ai
generate_product_content_using_ai
else
cover_thread = Thread.new { generate_product_cover_and_thumbnail_using_ai }View on GitHub (pinned to afeacbd394)
Solutions
- If you just changed the product's currency, also clear or re-pick the default offer code in the same save — a currency-scoped code will keep failing otherwise.
- Edit the offer code to include the product / drop the currency restriction / remove the exclusion, then save the product again.
- Select a universal code without a currency_type, or one explicitly attached to this product.
- Pre-check `offer_code.applicable?(@product)` before sending `default_offer_code_id` from API clients.
Example fix
# before — currency flip + stale code id in one save
put product_path(@product), params: { product: { price_currency_type: "eur", default_offer_code_id: usd_only_code.external_id } }
# after — clear the detached default in the same request
put product_path(@product), params: { product: { price_currency_type: "eur", default_offer_code_id: nil } } Defensive patterns
Strategy: validation
Validate before calling
code = @product.user.offer_codes.alive.find_by(external_id: default_offer_code_id) @product.default_offer_code = code if code&.applicable?(@product)
Type guard
def code_applies_after_change?(code, product, new_currency) return false unless code.applicable?(product) code.currency_type.blank? || code.currency_type == new_currency end
Try / catch
begin @product.update!(permitted_params) rescue Link::LinkInvalid => e # message is user-ready; prompt to re-pick or clear the default offer code render :edit, alert: e.message end
Prevention
- When changing a product's currency, always clear/re-pick its default offer code in the same request.
- This path has no unchanged-id exemption — keep default codes currency-free or currency-synced with the product.
- Re-check code applicability whenever you edit a code's product scope or exclusions.
- Mirror the bundles controller's unchanged-id early return if you extend this controller.
When it happens
Trigger: Saving a product whose currency was changed while a currency-scoped universal code stays set as default; a code whose product list no longer includes the product; a universal code that now excludes the product; selecting a code belonging to other products.
Common situations: Currency change on a product with a currency-restricted default offer code (the exact case the bundles controller comments about, still live here); product removed from a multi-product code's scope; stale editor tabs.
Related errors
- Offer code cannot be expired
- Offer code must apply to this product
- Offer code cannot be expired
- Invalid offer code
- Invalid offer code
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/3fbd7819e8605153.
Report an issue: GitHub.