antiwork/gumroad · error · Link::LinkInvalid

Cannot switch to per-variant content: the product has no var

Error message

Cannot switch to per-variant content: the product has no variants to migrate content to.

What it means

Link::LinkInvalid raised by migrate_to_per_variant_rich_content! when has_same_rich_content_for_all_variants changes to false while the product has product-level alive rich contents but zero alive variants. Per-variant mode stores content on variants; with no variants there is no destination for the migration (destination_variant = variants.first would be nil), so the switch is rejected.

Source

Thrown at app/controllers/api/v2/links_controller.rb:1023

        @product.alive_rich_contents.each(&:mark_deleted!)
      end
    end

    def clear_variant_rich_content!(retire_upsells: true)
      @product.alive_variants.each do |variant|
        retire_upsells_from_rich_contents!(variant.alive_rich_contents) if retire_upsells
        variant.alive_rich_contents.each(&:mark_deleted!)
        variant.product_files = []
      end
    end

    def migrate_to_per_variant_rich_content!
      product_pages = @product.alive_rich_contents.sort_by(&:position)
      return if product_pages.empty?

      variants = @product.alive_variants
      if variants.empty?
        raise Link::LinkInvalid, "Cannot switch to per-variant content: the product has no variants to migrate content to."
      end

      destination_variant = variants.first
      variants.each do |variant|
        variant.alive_rich_contents.each(&:mark_deleted!)
        variant.product_files = []
      end

      created = product_pages.each_with_index.map do |rc, index|
        destination_variant.alive_rich_contents.create!(title: rc.title, description: rc.description_without_stale_dead_cross_product_file_embeds, position: index)
      end
      file_ids = created.flat_map { _1.embedded_product_file_ids_in_order }.uniq
      destination_variant.product_files = file_ids.any? ? @product.product_files.alive.where(id: file_ids) : []

      product_pages.each(&:mark_deleted!)
    end

    def canonicalize_rich_contents(entity)

View on GitHub (pinned to afeacbd394)

Solutions

  1. Create at least one variant first, then switch the flag — the product-level pages migrate onto that variant.
  2. Or clear product-level content first if you truly want per-variant mode with content added later per variant.
  3. Order your API calls: create variants -> flip flag -> write variant content.
  4. Check alive_variants (soft-deleted variants don't count) before attempting the switch.

Example fix

# before: switch with no variants -> rejected
LinkApi.update(link_id, has_same_rich_content_for_all_variants: false)
# after: create the variant destination first
LinkApi.create_variant(link_id, name: "Standard")
LinkApi.update(link_id, has_same_rich_content_for_all_variants: false)
Defensive patterns

Strategy: validation

Validate before calling

raise InvalidTransition, "create a variant first" if product.alive_variants.empty? && product.alive_rich_contents.any?

Type guard

def safe_to_switch_to_per_variant?(product)
  product.alive_rich_contents.empty? || product.alive_variants.any?
end

Prevention

When it happens

Trigger: PATCH /api/v2/links/:id setting has_same_rich_content_for_all_variants: false on a product that has alive product-level content and alive_variants.empty? — e.g. all variants were deleted but product pages remain.

Common situations: Sellers deleting every variant (product becomes single-tier) then toggling the content mode; API scripts flipping the flag before creating variants; products reduced to one SKU where variants were soft-deleted.

Related errors


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