antiwork/gumroad · error · ThirdPartyAnalytic::ThirdPartyAnalyticInvalid

Only one analytics block is allowed per product. Please…

Error message

Only one analytics block is allowed per product. Please consolidate your tracking segments by pasting them into in a single block.

What it means

Raised as ThirdPartyAnalyticInvalid (app/models/third_party_analytic.rb:19) by ThirdPartyAnalytic.save_third_party_analytics when the submitted params contain two analytics entries with the same :location and the same product — the method tracks (location, product_id) pairs in product_hash and refuses the second. One analytics block per product per location (e.g. one for the product page, one for checkout) is the invariant, since each block is injected as a single segment in the page.

Solutions

  1. Dedupe analytics_params by [location, product] client-side before submitting; keep last-write-wins.
  2. Prevent adding a second block for the same product+location in the settings UI.
  3. Server-side, group params with params.group_by { |a| [a[:location], a[:product]] } and reject/merge duplicates before calling save_third_party_analytics.
  4. Rescue ThirdPartyAnalyticInvalid and show its message.

Example fix

# before
ThirdPartyAnalytic.save_third_party_analytics(analytics_params, seller) # ThirdPartyAnalyticInvalid

# after: last block per (location, product) wins
normalized = analytics_params.uniq { |a| [a[:location], a[:product]] }
ThirdPartyAnalytic.save_third_party_analytics(normalized, seller)
Defensive patterns

Strategy: validation

Validate before calling

keys = analytics_params.map { |a| [a[:location], a[:product]] }
return { error: 'One block per product+location.' } if keys.uniq.size != keys.size

Try / catch

begin
  ThirdPartyAnalytic.save_third_party_analytics(analytics_params, seller)
rescue ThirdPartyAnalytic::ThirdPartyAnalyticInvalid => e
  render json: { message: e.message }, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: POST analytics settings with analytics_params where two entries share both :location and :product (same permalink resolving to the same link) — e.g. duplicate rows appended by the settings form, or per-product blocks duplicated when a form state is applied twice.

Common situations: Analytics settings UI letting the same product+location row be added twice; integrations syncing tracking blocks that append instead of replace; product_from_permalink resolving two different permalink strings to the same product.

Related errors


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

Appendix: source

Thrown at app/models/third_party_analytic.rb:34

  FOR_ALL_PRODUCTS = "#all_products"
  LOCATIONS = ["all", "product", "receipt"]

  class ThirdPartyAnalyticInvalid < StandardError
  end

  def self.save_third_party_analytics(analytics_params, seller)
    existing_analytics = seller.third_party_analytics.alive
    keep_analytics = []

    if analytics_params.empty?
      existing_analytics.each(&:mark_deleted)
      return []
    end

    product_hash = Hash.new { |hash, key| hash[key] = {} }
    analytics_params.each do |third_party_analytic|
      product = product_from_permalink(third_party_analytic[:product])
      raise ThirdPartyAnalyticInvalid, "Only one analytics block is allowed per product. Please consolidate your tracking segments by pasting them into in a single block." if product_hash[third_party_analytic[:location]][product.try(:id)]

      product_hash[third_party_analytic[:location]][product.try(:id)] = true
    end

    analytics_params.each do |third_party_analytic|
      product = product_from_permalink(third_party_analytic[:product])

      if third_party_analytic[:id].present?
        analytics = seller.third_party_analytics.alive.find_by_external_id(third_party_analytic[:id])
        next if analytics.nil?

        analytics.link = product if analytics.link != product
        analytics.name = third_party_analytic[:name] if third_party_analytic[:name].present?
        analytics.location = third_party_analytic[:location] if third_party_analytic[:location].present?
        analytics.analytics_code = third_party_analytic[:code] if analytics.analytics_code != third_party_analytic[:code]
        analytics.save!
      else
        analytics = seller.third_party_analytics.create!(analytics_code: third_party_analytic[:code], name: third_party_analytic[:name], location: third_party_analytic[:location], link: product)

View on GitHub (pinned to afeacbd394)