Summary
TS defines a slot on a ...-container div alongside the publisher's own inner-div slot on the same GAM unit path (/88059007/prospect-a/homepage), yielding ~9 registered slots for ~3-4 real placements. Risk of competing defineSlot / double ad requests.
Evidence
googletag.pubads().getSlots() shows both ad-header-0-_R_...-container (TS) and ad-header-0-_r_1_ (publisher) for the same unit; same pattern for fixed_bottom and in_content. Observed on a live Next.js App Router publisher with TS enabled.
Verification — confirmed production impact
A fresh headless Chrome capture of the live Autoblog homepage on 2026-07-24 confirmed this is not just duplicate getSlots() bookkeeping: both slot sets issued successful GAM requests.
- At
16:52:49.929Z, GAM received a successful SRA request for three TS-owned container slots, all carrying ts_initial=1:
ad-header-0-_R_…-container
ad-fixed_bottom-0-_R_…-container
ad-in_content-…-in_content-0-container
- At
16:52:53.419Z (~3.5 seconds later), GAM received another successful SRA request for publisher-owned inner slots, including:
ad-header-0-_r_1_
ad-fixed_bottom-0-_r_f_
- publisher
in_content slots and the interstitial
- GPT emitted
slotRequested for both groups. The initial TS in_content slot and the later publisher header/fixed-bottom slots returned non-empty renders.
SRA combines slots within each request, but these are separate GAM requests. Header and fixed-bottom are clearly requested twice for the same semantic placement. The content element IDs change during React hydration, so the exact one-to-one content mapping needs a DOM-lifecycle trace; it does not change the confirmed header/footer duplication.
Root cause
The current TS implementation deliberately takes this path when the publisher has not yet defined the inner slot:
// Use outer container div for TS's slot when publisher hasn't defined
// theirs yet — keeps both slots on separate divs so publisher's
// later defineSlot on the inner div doesn't conflict.
const containerEl = document.getElementById(`${actualDivId}-container`);
const slotDivId = containerEl?.id ?? actualDivId;
const defined = g.defineSlot?.(slot.gam_unit_path, slot.formats, slotDivId);
Current source, lines 503–556 then calls display() for the TS-defined slot, whereas reused publisher slots are refreshed. This intentionally prevents a GPT conflict but creates two independently requestable slots when the publisher later defines the inner div.
Impact
- Extra GAM requests and slot lifecycle work for the same user-visible placement.
- Potential duplicate auction/creative rendering and viewability/revenue-reporting distortion.
- Extra page/network cost. A duplicate GAM ad-unit path is valid GPT usage when it represents genuinely distinct placements; here the header and fixed-bottom pairs are not distinct placements.
Acceptance
- One registered/requested GPT slot per placement, or
- an explicitly documented dual-ownership design that guarantees only one of the container/inner slots is displayed or refreshed and therefore only one GAM request is sent per placement.
Suggested regression coverage
Model the observed ordering: TS adInit() executes before the publisher’s defineSlot(innerDiv). Assert that the later publisher definition cannot result in a second displayed/refreshed GAM slot for the same configured placement, and capture the corresponding slotRequested events (not only getSlots()).
Summary
TS defines a slot on a
...-containerdiv alongside the publisher's own inner-div slot on the same GAM unit path (/88059007/prospect-a/homepage), yielding ~9 registered slots for ~3-4 real placements. Risk of competingdefineSlot/ double ad requests.Evidence
googletag.pubads().getSlots()shows bothad-header-0-_R_...-container(TS) andad-header-0-_r_1_(publisher) for the same unit; same pattern forfixed_bottomandin_content. Observed on a live Next.js App Router publisher with TS enabled.Verification — confirmed production impact
A fresh headless Chrome capture of the live Autoblog homepage on 2026-07-24 confirmed this is not just duplicate
getSlots()bookkeeping: both slot sets issued successful GAM requests.16:52:49.929Z, GAM received a successful SRA request for three TS-owned container slots, all carryingts_initial=1:ad-header-0-_R_…-containerad-fixed_bottom-0-_R_…-containerad-in_content-…-in_content-0-container16:52:53.419Z(~3.5 seconds later), GAM received another successful SRA request for publisher-owned inner slots, including:ad-header-0-_r_1_ad-fixed_bottom-0-_r_f_in_contentslots and the interstitialslotRequestedfor both groups. The initial TSin_contentslot and the later publisher header/fixed-bottom slots returned non-empty renders.SRA combines slots within each request, but these are separate GAM requests. Header and fixed-bottom are clearly requested twice for the same semantic placement. The content element IDs change during React hydration, so the exact one-to-one content mapping needs a DOM-lifecycle trace; it does not change the confirmed header/footer duplication.
Root cause
The current TS implementation deliberately takes this path when the publisher has not yet defined the inner slot:
Current source, lines 503–556 then calls
display()for the TS-defined slot, whereas reused publisher slots are refreshed. This intentionally prevents a GPT conflict but creates two independently requestable slots when the publisher later defines the inner div.Impact
Acceptance
Suggested regression coverage
Model the observed ordering: TS
adInit()executes before the publisher’sdefineSlot(innerDiv). Assert that the later publisher definition cannot result in a second displayed/refreshed GAM slot for the same configured placement, and capture the correspondingslotRequestedevents (not onlygetSlots()).