antiwork/gumroad · error · Link::LinkInvalid

All tiers must have the same set of payment options.

Error message

All tiers must have the same set of payment options.

What it means

The final pass of validate_variant_recurrences!: each variant's enabled recurrence keys (from recurrence_price_values, selected and sorted) are compared pairwise across tiers; the first adjacent mismatch (e.g. ['monthly','yearly'] vs ['monthly']) adds the error and raises Link::LinkInvalid. The product enforces that every tier exposes exactly the same set of payment options — mixed option sets across tiers are not representable.

Source

Thrown at app/services/product/variant_category_updater_service.rb:618

        )
          errors.add(:base, "Please provide a price for the default payment option.")
          raise Link::LinkInvalid, "Please provide a price for the default payment option."
        end
      end

      # error if variants have different recurrence options:
      # 1. Extract variant recurrence selections:
      # Ex. [["monthly", "yearly"], ["monthly"]]
      enabled_recurrences_for_variants = variants.map do |variant|
        variant[:recurrence_price_values].select { |k, v| v[:enabled] }.keys.sort
      end
      # 2. Ensure that they match
      # Ex. ["monthly", "yearly"] != ["monthly"] raises error
      enabled_recurrences_for_variants.each_with_index do |recurrences, index|
        next_recurrences = enabled_recurrences_for_variants[index + 1]
        if next_recurrences && recurrences != next_recurrences
          errors.add(:base, "All tiers must have the same set of payment options.")
          raise Link::LinkInvalid, "All tiers must have the same set of payment options."
        end
      end
    end
end

View on GitHub (pinned to afeacbd394)

Solutions

  1. Open each tier's pricing grid and check exactly the same recurrence options on all tiers
  2. Or disable the extra option on every tier so the sets match
  3. Re-save after aligning; the pairwise comparison reports the first mismatched pair, but auditing all tiers at once is faster

Example fix

# before
tier1_recurrences = { 'monthly' => {...}, 'yearly' => {...} }
tier2_recurrences = { 'yearly' => {...} } # mismatch
# => Link::LinkInvalid: All tiers must have the same set of payment options.

# after
tier2_recurrences = { 'monthly' => {...}, 'yearly' => {...} } # same enabled set
Defensive patterns

Strategy: validation

Validate before calling

enabled_sets = variants.map { |v| v[:recurrence_price_values].select { |_k, val| val[:enabled] }.keys.sort }
raise 'tiers have different payment options' unless enabled_sets.uniq.one?

Type guard

def all_tiers_same_recurrence_options?(variants)
  variants.map { |v| v[:recurrence_price_values].select { |_k, val| val[:enabled] }.keys.sort }.uniq.one?
end

Try / catch

begin
  VariantCategoryUpdaterService.new(product:, category_params:).process
rescue Link::LinkInvalid
  render_edit_form_with(product.errors.full_messages)
end

Prevention

When it happens

Trigger: Tier 1 enables monthly+yearly while Tier 2 enables only yearly; any tier disabling an option that the others keep enabled.

Common situations: Adding a new tier later and forgetting to check both periods; toggling one period off on a single tier in the editor grid.

Related errors


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