Always emit hb_adid so server-side ad template creatives render - #996
Open
prk-Jr wants to merge 1 commit into
Open
Always emit hb_adid so server-side ad template creatives render#996prk-Jr wants to merge 1 commit into
prk-Jr wants to merge 1 commit into
Conversation
…e absent Bidders that return neither a Prebid Cache UUID nor an `adid` produced no `hb_adid` in `window.tsjs.bids` at all. `adInit` only sets targeting keys that exist on the bid, so GAM never received an `hb_adid` key, the Universal Creative's `%%PATTERN:hb_adid%%` expanded to empty, and the render bridge rejected the resulting `Prebid Request` message for want of an ad ID. The line item won and served its wrapper, but the creative never rendered. Add `Bid::bid_id`, populated from the OpenRTB bid object's own `id`, and use it as the last-resort `hb_adid` source. Per spec `id` is mandatory, so this closes the gap for every bidder. It is unique per bid instance rather than a creative identifier, which is exactly what `hb_adid` needs here: a stable value GAM echoes back verbatim so the bridge can find this winning bid. `cache_id` and `ad_id` keep priority in that order — locked in by test, since the Universal Creative treats `hb_adid` as the Prebid Cache lookup key whenever `hb_cache_host`/`hb_cache_path` are present. `bid_id` is carried as its own field rather than folded into `ad_id`, which is exposed raw in the debug bid and would mislead consumers treating it as a creative identifier. Verified: cargo fmt, all six clippy targets, test-fastly / test-axum / test-cloudflare / test-spin, the parity suite, JS vitest, and JS + docs prettier checks all pass. The new bid_map test was confirmed to fail with the fallback removed.
prk-Jr
requested review from
ChristianPavilonis and
aram356
and removed request for
aram356
August 4, 2026 16:51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
adidproduced nohb_adidinwindow.tsjs.bids.adInitonly sets targeting keys that exist on the bid, so GAM never received anhb_adidkey, the Universal Creative's%%PATTERN:hb_adid%%expanded to empty, and the render bridge rejected the resultingPrebid Requestfor want of an ad ID. The line item won and served its wrapper, but the creative never rendered — a silent, per-bidder loss of every SSAT impression.Bid::bid_id, populated from the OpenRTB bid object's ownid, and uses it as the last-resorthb_adidsource.idis mandatory per the OpenRTB spec, so this closes the gap for every bidder.cache_idandad_idkeep priority, in that order. This is not cosmetic: whenhb_cache_host/hb_cache_pathare present the Universal Creative useshb_adidas the Prebid Cache lookup key (https://<host><path>?uuid=<hb_adid>), so an arbitrary value there would break cache retrieval. Both priorities are locked in by test.Why a separate field rather than folding into
ad_idad_idis the creative/ad identifier and is exposed raw in the debug bid. The OpenRTBidis unique per bid instance, not per creative — surfacing it asad_idwould mislead any consumer treating that field as a creative identifier. The existing comment inparse_bidsays as much, so the bid ID is carried as its own field instead.Changes
crates/trusted-server-core/src/auction/types.rsBid::bid_id: Option<String>with#[serde(default, skip_serializing_if = "Option::is_none")], so the serialized wire shape is unchanged when absentcrates/trusted-server-core/src/integrations/prebid.rsbid_idfrom the OpenRTB bididinparse_bid; two tests covering it alongside and withoutadid/cachecrates/trusted-server-core/src/publisher.rsbuild_bid_maphb_adidchain becomescache_id → ad_id → bid_id; test for the new fallback, renamed omit-case test, andbid_idadded to the cache/ad-id tests to prove precedencecrates/trusted-server-core/src/auction/orchestrator.rsbid_id: NoneatBidliterals (tests)crates/trusted-server-core/src/auction/formats.rsbid_id: NoneatBidliteralcrates/trusted-server-core/src/auction/telemetry.rsbid_id: NoneatBidliteral (test)crates/trusted-server-core/src/integrations/adserver_mock.rsbid_id: NoneatBidliteralscrates/trusted-server-core/src/integrations/aps.rsbid_id: NoneatBidliteralCHANGELOG.mdTest plan
cargo test-fastly && cargo test-axumcargo clippy-fastly && cargo clippy-axumcargo fmt --all -- --checkcd crates/trusted-server-js/lib && npx vitest runcd crates/trusted-server-js/lib && npm run formatcd docs && npm run formatcargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1fastly compute servecargo test-cloudflare,cargo test-spin,cargo clippy-cloudflare,cargo clippy-cloudflare-wasm,cargo clippy-spin-native,cargo clippy-spin-wasm, and the parity suite (cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity)The new
bid_map_falls_back_to_bid_id_when_cache_id_and_ad_id_absenttest was confirmed to fail with the fallback removed (left: None), so it genuinely gates the fix rather than passing vacuously.Not covered by automated tests: the browser-side half of the loop — that GAM echoes the new
hb_adidback and the bridge serves the creative. That path is the subject of #926.Checklist
unwrap()in production code — useexpect("should ...")tracingmacros (notprintln!)