antiwork/gumroad · error · Subscription::UpdateFailed
Installment plans cannot be updated.
Error message
Installment plans cannot be updated.
What it means
Raised as Subscription::UpdateFailed (app/models/subscription.rb:10) at the top of Subscription#update_current_plan! when the subscription is an installment plan (is_installment_plan?). update_current_plan! works by creating a new original subscription purchase and archiving the old one — a migration that has no defined meaning for installment plans, so it is refused up front. Callers in the codebase (Subscription::UpdaterService, RecurringChargeWorker, ScheduleMembershipPriceUpdatesJob) rescue Subscription::UpdateFailed explicitly.
Source
Thrown at app/models/subscription.rb:1032
end
end
def end_subscription!
with_lock do
return if ended_at.present?
self.ended_at = Time.current
self.deactivate!
CustomerLowPriorityMailer.subscription_ended(id).deliver_later(queue: "low")
ContactingCreatorMailer.subscription_ended(id).deliver_later(queue: "critical") if seller.enable_payment_email?
end
end
# creates a new original subscription purchase & archives the existing one.
# Any changes to the subscription made here must be reverted in `Subscription::UpdaterService#restore_original_purchase`
def update_current_plan!(new_variants:, new_price:, new_quantity: nil, perceived_price_cents: nil, is_applying_plan_change: false, skip_preparing_for_charge: false, offer_code: nil, clear_discount: false, clear_deleted_discount: false, authenticated_offer_code_buyer: AUTHENTICATED_OFFER_CODE_BUYER_NOT_PROVIDED, submitted_pre_discount_price_cents: nil, once_per_cart_discount_allocation: nil)
raise Subscription::UpdateFailed, "Installment plans cannot be updated." if is_installment_plan?
raise Subscription::UpdateFailed, "Changing plans for fixed-length subscriptions is not currently supported." if has_fixed_length?
ActiveRecord::Base.transaction do
payment_option = last_payment_option
# build new original subscription purchase
new_purchase = build_purchase(override_params: { is_original_subscription_purchase: true,
email: original_purchase.email,
is_free_trial_purchase: original_purchase.is_free_trial_purchase },
authenticated_offer_code_buyer:)
# avoid failing `Purchase#variants_available` validation if reverting back to the original set of variants & those variants are unavailable
new_purchase.original_variant_attributes = original_purchase.variant_attributes
# avoid failing `Purchase#price_not_too_low` validation if reverting back to the original subscription price & price has been deleted
new_purchase.original_price = price
# avoid `Purchase#not_double_charged` and sold out validations
new_purchase.is_updated_original_subscription_purchase = true
# avoid price validation failures when applying a pre-existing plan change (i.e. a downgrade)
new_purchase.is_applying_plan_change = is_applying_plan_changeView on GitHub (pinned to afeacbd394)
Solutions
- Filter plan-change flows on !subscription.is_installment_plan? before calling update_current_plan! — installment plans don't support mid-flight plan changes.
- For installment-plan members who want a different plan, cancel and repurchase instead.
- In jobs/services, rescue Subscription::UpdateFailed as the existing callers do and record/report it rather than letting it crash the worker.
Example fix
# before
subscription.update_current_plan!(new_variants:, new_price:) # Subscription::UpdateFailed
# after
if subscription.is_installment_plan?
return { error: "Installment plans cannot be updated." }
end
subscription.update_current_plan!(new_variants:, new_price:) Defensive patterns
Strategy: try-catch
Validate before calling
return { unsupported: true } if subscription.is_installment_plan? Try / catch
begin subscription.update_current_plan!(new_variants:, new_price:) rescue Subscription::UpdateFailed => e # same pattern as Subscription::UpdaterService / RecurringChargeWorker log_and_report(e) # installment plans cannot change plans mid-flight end
Prevention
- Filter plan-change flows on !is_installment_plan? at the entry point.
- Offer cancel-and-repurchase for installment-plan members instead.
- Price-update jobs must skip installment plans or rescue UpdateFailed.
When it happens
Trigger: Invoking update_current_plan! (the change-plan path used by Subscription::UpdaterService and price-update jobs) on a subscription where is_installment_plan? is true — e.g. a member's plan-change request hitting an installment-plan subscription.
Common situations: Product mixes installment-plan and normal memberships and the change-plan endpoint doesn't filter; scheduled membership price-update jobs sweeping all subscriptions including installment plans; UI offering 'change tier' on installment purchases.
Related errors
- Changing plans for fixed-length subscriptions is not current
- india_card_mandate_missing
- upi_recurring_authorization_required
- upi_recurring_authorization_required
- This commission has already been completed, so its files can
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/ae9966f6606f55f2.
Report an issue: GitHub.