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
- Create at least one variant first, then switch the flag — the product-level pages migrate onto that variant.
- Or clear product-level content first if you truly want per-variant mode with content added later per variant.
- Order your API calls: create variants -> flip flag -> write variant content.
- 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
- Create the destination variant before flipping has_same_rich_content_for_all_variants to false.
- Clear product-level content first if you want per-variant mode without migration.
- Count only alive variants — soft-deleted ones don't provide a destination.
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
- Cannot switch to shared content: both product-level and vari
- Cannot switch to shared content: multiple variants have dist
- Cannot update product-level rich content while in per-varian
- Cannot update variant rich content while the product uses sh
- File embeds reference files not belonging to this product: #
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/b397a1376391d3a9.
Report an issue: GitHub.