antiwork/gumroad · error · Purchase::PurchaseInvalid

Sorry, this offer is no longer available.

Error message

Sorry, this offer is no longer available.

What it means

When params[:accepted_offer] is present, Purchase::CreateService looks the upsell up in Upsell.available_to_customers.find_by_external_id(params[:accepted_offer][:id]). If no available upsell matches (not found, no longer available_to_customers, expired, or deleted by the seller), Purchase::PurchaseInvalid is raised and the purchase does not proceed with the offer.

Source

Thrown at app/services/purchase/create_service.rb:113

        end
      end

      if purchase.offer_code&.minimum_amount_cents.present?
        valid_items = params[:cart_items]
        valid_items = if purchase.offer_code.universal
          excluded_permalinks = purchase.offer_code.excluded_products.pluck(:unique_permalink)
          valid_items.reject { excluded_permalinks.include?(_1[:permalink]) }
        else
          valid_items.filter { purchase.offer_code.products.find_by(unique_permalink: _1[:permalink]).present? }
        end
        if valid_items.map { _1[:price_cents].to_i }.sum < purchase.offer_code.minimum_amount_cents
          raise Purchase::PurchaseInvalid, "Sorry, you have not met the offer code's minimum amount."
        end
      end

      if params[:accepted_offer].present?
        upsell = Upsell.available_to_customers.find_by_external_id(params[:accepted_offer][:id])
        raise Purchase::PurchaseInvalid, "Sorry, this offer is no longer available." unless upsell.present?
        if upsell.cross_sell?
          if upsell.not_replace_selected_products?
            cart_product_permalinks = params[:cart_items].reject { _1[:permalink] == product.unique_permalink }.map { _1[:permalink] }
            if upsell.not_is_content_upsell? && (upsell.universal ? product.user.products : upsell.selected_products).where(unique_permalink: cart_product_permalinks).empty?
              raise Purchase::PurchaseInvalid, "The cart does not have any products to which the upsell applies."
            end
          end

          # The original discount is retained if it is better than the upsell
          # discount. The client can't automatically set the upsell discount
          # because it doesn't have a "code". Thus, upsell discount should only
          # be applied when the purchase does not already have a discount code.
          if purchase.offer_code.blank? && upsell.offer_code&.evaluate_for_buyer(buyer, product: purchase.link).present? &&
             (!params[:is_purchasing_power_parity_discounted] || perceived_price_matches_accepted_offer?(upsell.offer_code))
            purchase.offer_code = upsell.offer_code
          end
        end
        purchase.build_upsell_purchase(

View on GitHub (pinned to afeacbd394)

Solutions

  1. Refresh the checkout page — if the offer is still live it will be re-offered with a valid reference
  2. Otherwise complete the purchase without the upsell; the base purchase is unaffected
  3. If building a client, treat accepted_offer as optional and re-validate availability before submitting
Defensive patterns

Strategy: fallback

Validate before calling

if params[:accepted_offer].present?
  upsell = Upsell.available_to_customers.find_by_external_id(params[:accepted_offer][:id])
  raise 'offer no longer available' if upsell.nil?
end

Type guard

def accepted_offer_still_available?(offer_id)
  Upsell.available_to_customers.exists?(external_id: offer_id)
end

Try / catch

begin
  Purchase::CreateService.new(user: buyer, params: purchase_params).perform
rescue Purchase::PurchaseInvalid => e
  if e.message.include?('no longer available')
    purchase_params.delete(:accepted_offer) # complete the base purchase without the upsell
    retry
  else
    raise
  end
end

Prevention

When it happens

Trigger: Accepting an upsell that expired or was deleted between when the checkout page rendered and when the buyer clicked accept; a stale checkout tab; a wrong or tampered accepted_offer id in the request.

Common situations: Time-limited upsell offers; sellers editing/deleting upsells while carts are open; long-lived checkout pages on mobile.

Related errors


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