antiwork/gumroad · error

Sorry, something went wrong. Please try again.

Error message

Sorry, something went wrong. Please try again.

What it means

Purchase::CreateService serializes checkouts of products that don't allow parallel purchases with a Redis-backed inventory semaphore (acquisition timeout 50 seconds, INVENTORY_LOCK_ACQUISITION_TIMEOUT). If semaphore.lock returns nil — the per-product lock stayed held for the entire window — perform returns [nil, "Sorry, something went wrong. Please try again."] after a warn log naming the product id. Nothing was charged; this is pure lock contention, not a payment failure.

Source

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

  attr_reader :product, :params, :purchase_params, :gift_params, :buyer
  attr_accessor :purchase, :gift

  def initialize(product:, params:, buyer: nil)
    @product = product
    @params = params
    @purchase_params = params[:purchase]
    @gift_params = params[:gift].presence
    @buyer = buyer
    @force_new_subscription = !!params[:force_new_subscription]
  end

  def perform
    unless @product.allow_parallel_purchases?
      inventory_semaphore = inventory_lock_client
      inventory_lock_token = inventory_semaphore.lock
      if inventory_lock_token.nil?
        Rails.logger.warn("Could not acquire lock for product_inventory semaphore (product id: #{@product.id})")
        return nil, "Sorry, something went wrong. Please try again."
      end
    end

    begin
      # create gift if necessary
      self.gift = create_gift if is_gift?

      # run pre-build validations
      validate_perceived_price
      validate_zip_code

      # build primary (non-gift) purchase
      self.purchase = build_purchase(purchase_params.merge(gift_given: gift))
      purchase.submitted_pre_discount_price_cents = params[:submitted_pre_discount_price_cents]
      purchase.once_per_cart_discount_allocation = params[:once_per_cart_discount_allocation]
      if purchase.once_per_cart_discount_allocation.present?
        purchase.offer_code = OfferCode.find_by(id: purchase.once_per_cart_discount_allocation[:offer_code_id])
      end

View on GitHub (pinned to afeacbd394)

Solutions

  1. Retry the purchase — the lock releases when the in-flight checkout finishes and the next attempt acquires it.
  2. If it persists, check Redis health and whether the semaphore for the product id in the warn log has leaked (holder crashed without release).
  3. Debounce or disable the buy button client-side to cut duplicate concurrent attempts.
Defensive patterns

Strategy: retry

Validate before calling

# Cheap pre-check: fail fast (or queue) when contention is likely
locked = inventory_lock_client.lock unless @product.allow_parallel_purchases?
return retry_later if locked.nil? # instead of burning the full 50s window

Try / catch

# perform returns a tuple, not an exception
purchase, error = Purchase::CreateService.new(product:, params:, buyer:).perform
if error == "Sorry, something went wrong. Please try again." && purchase.nil?
  retry_with_backoff # lock contention is transient; nothing was charged
end

Prevention

When it happens

Trigger: Two or more concurrent checkouts of a limited-inventory product where an earlier holder keeps the lock for the full 50s (slow charge under load), a previous holder crashed so the lock waits out its TTL, Redis unavailable or slow, or a flash-sale burst exceeding what the semaphore admits.

Common situations: Product drops and flash sales on limited products; buyer double-clicks; retry storms after a partial outage; elevated charge-processor latency stretching each holder's critical section.

Related errors


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