antiwork/gumroad · error · Link::LinkInvalid
Cannot switch to shared content: multiple variants have dist
Error message
Cannot switch to shared content: multiple variants have distinct content. Remove variant content first, or send replacement rich_content in the same request.
What it means
Link::LinkInvalid raised by migrate_to_shared_rich_content! when switching to shared content: more than one variant has alive rich contents and they are not identical after canonicalization (canonicalize_rich_contents normalizes for comparison). Shared mode renders one content set for all variants, so distinct variant contents cannot be auto-merged — the API names the conflict and requires deduplication or replacement content.
Source
Thrown at app/controllers/api/v2/links_controller.rb:981
if @product.has_same_rich_content_for_all_variants?
migrate_to_shared_rich_content!
else
migrate_to_per_variant_rich_content!
end
end
def migrate_to_shared_rich_content!
variants_with_content = @product.alive_variants.select { |v| v.alive_rich_contents.any? }
if @product.alive_rich_contents.any? && variants_with_content.any?
raise Link::LinkInvalid, "Cannot switch to shared content: both product-level and variant-level content exist. Remove one side first, or send replacement rich_content in the same request."
end
if variants_with_content.length > 1
canonical = canonicalize_rich_contents(variants_with_content.first)
all_identical = variants_with_content.all? { |v| canonicalize_rich_contents(v) == canonical }
if !all_identical
raise Link::LinkInvalid, "Cannot switch to shared content: multiple variants have distinct content. Remove variant content first, or send replacement rich_content in the same request."
end
end
source_variant = nil
if @product.alive_rich_contents.empty? && variants_with_content.any?
source_variant = variants_with_content.first
source_variant.alive_rich_contents.sort_by(&:position).each_with_index do |rc, index|
@product.alive_rich_contents.create!(title: rc.title, description: rc.description_without_stale_dead_cross_product_file_embeds, position: index)
end
end
@product.alive_variants.each do |variant|
retire_upsells_from_rich_contents!(variant.alive_rich_contents) if variant != source_variant
variant.alive_rich_contents.each(&:mark_deleted!)
variant.product_files = []
end
end
View on GitHub (pinned to afeacbd394)
Solutions
- Pick the winning variant's content: delete the other variants' content, then switch the flag (the remaining single variant migrates up).
- Or send rich_content in the same switch request to explicitly define the shared content.
- If you believe the contents are equivalent, diff them canonically (embeds ids, page breaks, titles) to find the real difference.
- Bulk tooling: only auto-switch products where variants_with_content.length <= 1 or contents canonicalize equal.
Example fix
# before: distinct variant contents + flag flip -> rejected LinkApi.update(link_id, has_same_rich_content_for_all_variants: true) # after: choose winner explicitly variant_b_contents.each(&:delete) LinkApi.update(link_id, has_same_rich_content_for_all_variants: true) # variant A's content migrates up
Defensive patterns
Strategy: validation
Validate before calling
contents = product.alive_variants.map(&:alive_rich_contents)
canonical = contents.map { |set| canonicalize(set) }.uniq
auto_switch_only_if(contents.length <= 1 || canonical.length == 1) Type guard
def variant_contents_identical?(product)
sets = product.alive_variants.map(&:alive_rich_contents).reject(&:empty?)
return true if sets.length <= 1
sets.map { |s| canonicalize_rich_contents(s) }.uniq.length == 1
end Prevention
- Before switching to shared, canonicalize and compare variant contents; only auto-switch when identical.
- Explicitly choose the winning variant's content otherwise.
- Include replacement rich_content in the switch request to skip auto-merge entirely.
When it happens
Trigger: PATCH /api/v2/links/:id flipping has_same_rich_content_for_all_variants to true (without replacement rich_content) on a product whose two-plus variants each have content that differs in any canonicalized way (text, embeds, ordering, page count).
Common situations: Sellers who customized content per variant (the whole point of per-variant mode) later merging; identical-looking pages that differ by an embed id or trailing whitespace caught by canonicalization; scripts assuming 'variants have same content' without comparing.
Related errors
- Cannot switch to shared content: both product-level and vari
- Cannot switch to per-variant content: the product has no var
- 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/57894144c89e4e4a.
Report an issue: GitHub.