antiwork/gumroad · error · Link::LinkInvalid
Cannot switch to shared content: both product-level and vari
Error message
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.
What it means
Link::LinkInvalid raised by migrate_to_shared_rich_content! when the product flips to shared content (has_same_rich_content_for_all_variants -> true) but BOTH product-level alive rich contents and at least one variant with alive rich contents exist. There is no unambiguous single source for the shared content, so the migration refuses instead of picking a winner silently; you must remove one side or send replacement rich_content with the switch.
Source
Thrown at app/controllers/api/v2/links_controller.rb:974
removed = existing_rich_contents - rich_contents_to_keep
retire_upsells_from_rich_contents!(removed)
removed.each(&:mark_deleted!)
end
def migrate_rich_content_for_flag_change!
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
View on GitHub (pinned to afeacbd394)
Solutions
- Delete the variant-level content (or the product-level pages) first, then switch the flag.
- Or send rich_content in the same request — replacement content wins and the both-sides ambiguity disappears.
- For bulk migrations, pre-scan products for the mixed state and reconcile each before flipping flags.
- Don't try to dodge it via raw DB updates — the same invariant is what the migration protects.
Example fix
# before: ambiguous switch LinkApi.update(link_id, has_same_rich_content_for_all_variants: true) # after: send replacement shared content in the same request LinkApi.update(link_id, has_same_rich_content_for_all_variants: true, rich_content: new_shared_pages)
Defensive patterns
Strategy: validation
Validate before calling
product = LinkApi.get(link_id)
both_sides = product.rich_contents.any? && product.variants.any? { |v| v.rich_contents.any? }
send_replacement_content! if both_sides # or delete one side first Type guard
def safe_to_switch_to_shared?(product)
variants_with_content = product.alive_variants.select { |v| v.alive_rich_contents.any? }
!(product.alive_rich_contents.any? && variants_with_content.any?)
end Prevention
- Reconcile mixed-content products before flipping the shared flag.
- Send replacement rich_content with the switch to remove ambiguity.
- Pre-scan products in bulk migrations for the both-sides state.
When it happens
Trigger: PATCH /api/v2/links/:id with has_same_rich_content_for_all_variants changing to true (and no replacement rich_content) on a product where @product.alive_rich_contents.any? && variants_with_content.any? — legacy mixed-state products hit this most.
Common situations: Products left in a mixed state by older flows or partial migrations; sellers who authored variant content while product-level content also exists; scripts toggling the flag without first reconciling content.
Related errors
- Cannot switch to shared content: multiple variants have dist
- 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/abfac29f80ec1381.
Report an issue: GitHub.