antiwork/gumroad · error · ActiveRecord::RecordInvalid

This commission's deposit is no longer in a completable stat

Error message

This commission's deposit is no longer in a completable state, so it can no longer be completed.

What it means

Raised as ActiveRecord::RecordInvalid by Commission#ensure_deposit_is_chargeable! (app/models/commission.rb:145) during the completion flow when deposit_is_chargeable? returns false. The check is deliberately strict: the commission must be is_in_progress?, the deposit purchase is re-read from the database (Purchase.find, not the memoized association) and must be in a successful state including test_successful, and it must not be refunded, stripe_partially_refunded, or chargedback_not_reversed. The fresh read exists because refunding the deposit is the documented way for sellers to reject a commission, and nothing transitions the commission when that refund lands through another instance.

Source

Thrown at app/models/commission.rb:149

    def once_per_cart_discounted_display_price_cents
      discount = deposit_purchase.purchase_offer_code_discount
      return unless discount&.once_per_cart? && !discount.offer_code_is_percent
      return if discount.pre_discount_displayed_price_cents.blank?

      total = [discount.pre_discount_displayed_price_cents - discount.offer_code_amount, 0].max
      minimum = deposit_purchase.link.currency["min_price"]
      total = minimum if total.positive? && total < minimum
      total
    end

    # Refunding the deposit is how the Help Center tells sellers to reject a commission, and
    # nothing transitions the commission when they do — so the deposit is re-read at charge time.
    def ensure_deposit_is_chargeable!
      return if deposit_is_chargeable?

      errors.add(:base, "This commission's deposit is no longer in a completable state, so it can no longer be completed.")
      raise ActiveRecord::RecordInvalid, self
    end

    def ensure_deliverable_is_attached!
      return if files.attached?

      errors.add(:base, "Attach at least one file before completing this commission.")
      raise ActiveRecord::RecordInvalid, self
    end

    def deposit_is_chargeable?
      return false unless is_in_progress?

      # A fresh read, not the memoized association — a refund can land through another instance
      # after this commission was loaded. `find` rather than `reload` because the completion
      # purchase prices variants from the memoized deposit's loaded association objects.
      deposit = Purchase.find(deposit_purchase_id)
      # Including test: a seller buying their own commission product gets a `test_successful`
      # deposit, and completing it is a supported flow that skips charging entirely.

View on GitHub (pinned to afeacbd394)

Solutions

  1. Before completing, re-check the same conditions the guard does: commission.is_in_progress? and the freshly-read deposit purchase's state (successful-including-test, not refunded/partially-refunded/charged-back).
  2. If the deposit was refunded intentionally, treat the commission as rejected — do not attempt completion; no state transition exists for refunds by design.
  3. Refresh the commission state in the UI before showing the Complete action so stale sessions don't attempt completion against a changed deposit.
  4. Handle ActiveRecord::RecordInvalid at the completion endpoint and return the model's errors.full_messages as a 422.

Example fix

# before
commission.complete! # RecordInvalid: deposit no longer completable

# after
def complete_if_chargeable!(commission)
  deposit = Purchase.find(commission.deposit_purchase_id)
  chargeable = commission.is_in_progress? &&
    Purchase::ALL_SUCCESS_STATES_INCLUDING_TEST.include?(deposit.purchase_state) &&
    !deposit.refunded? && !deposit.stripe_partially_refunded? && !deposit.chargedback_not_reversed?
  return { rejected: true } unless chargeable

  commission.complete!
end
Defensive patterns

Strategy: validation

Validate before calling

deposit = Purchase.find(commission.deposit_purchase_id)
chargeable = commission.is_in_progress? &&
  Purchase::ALL_SUCCESS_STATES_INCLUDING_TEST.include?(deposit.purchase_state) &&
  !deposit.refunded? && !deposit.stripe_partially_refunded? && !deposit.chargedback_not_reversed?
return reject_path unless chargeable

Try / catch

begin
  commission.complete!
rescue ActiveRecord::RecordInvalid
  render json: { errors: commission.errors.full_messages }, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: Calling the commission-complete endpoint when: the commission already left in_progress; the deposit Purchase.purchase_state is not in Purchase::ALL_SUCCESS_STATES_INCLUDING_TEST (failed/errored); or the deposit was refunded, partially refunded on Stripe, or charged back without reversal. Also hit when a refund lands between page load and clicking Complete — the guard re-reads the deposit at charge time and catches it.

Common situations: Seller refunds the deposit to reject the commission, then (or a colleague in another session) tries to complete it anyway. Stale UI still showing a Complete button after the buyer charged back the deposit. Completion racing an in-flight refund.

Related errors


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