Skip to content

Build1 publisher3 min readPublished

579 rows and not one payload key: a clean pipeline answering the wrong question

A one-browser ad measurement run found a boolean where the bid request body was supposed to be. No gate fired, because every gate had been written about the code's question.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying 579 rows and not one payload key: a clean pipeline answering the wrong question
Generated illustration

What happened

  • A single-operator, single-browser project attempting to read the author's own ad profile out of live bid requests found that 579 candidate request rows captured in one night retained not one key of the request payload.
  • On disk, the field that was supposed to hold what the request said was a boolean.
  • Three narrow parsers read post_data, extracted a verdict, and discarded the body.
  • The author had spent four days measuring prices and outcomes, including which bidders answer and which stay silent, before checking what the capture had kept of the payload.
  • The author names the failure mode: a pipeline can be perfectly honest, gated and reproducible while quietly answering a different question than the one asked, and no gate fires because every gate was written about the question the code actually answers.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

A single-operator measurement project that set out to read its own advertising profile out of live bid requests discovered that its capture had kept 579 candidate request rows from one night and not one key of the payload those rows were supposed to hold [1]. The field on disk that was meant to contain what the request said held a boolean instead [2], and nothing in the pipeline complained, which is the part worth copying into your own postmortem template.

According to the project's write-up, three narrow parsers read `post_data`, extracted a verdict, and discarded the body [3]. That is a coherent design if the question is "did this host bid." It happens to be the design you also get if you meant to ask "what did this host say about me," and four days of measuring prices, which bidders answer and which stay silent went by before the gap surfaced [4]. The author names the failure mode directly: a pipeline can be honest, gated and reproducible while quietly answering a different question than the one asked, because every gate was written about the question the code actually answers [5].

The second-order version of the same bug was worse. The first fix stored the request URL as `url[:500]` [6], so every exchange that asks by GET had its question cut mid-query-string, with nothing on the row recording the cut [7]. On a fresh sweep, 174 of 380 request rows exceeded 500 characters [8], just under half of them [9], and one `yandex.ru` request ran to 9529 characters [10], more than nineteen times the retained window [11]. `ssp01.rambler.ru` shows the cost: under the truncating schema it read as `no-body`, absent from every count [12], while its query string carries `adtech_uid`, `publisher_uid`, `rq_sess`, a `top100_session_id` map keyed by publisher site id, and six Adfox `puidN` targeting parameters [13]. A body-only reading of that market reports the opposite of what is present [14].

The corrected census reports key-path shapes and never values [15]. Restricted to paths the capture tool had already classified as bid candidates with at least four bodies read, all 18 receive the Adfox `places` protocol and nothing else: seven to thirteen key paths, every one about the placement, the format or the page [16]. On this path, for most of the demand side, the audience profile does not travel inside the bid request the browser sends; the enrichment happens server-side, past the client's vantage [17]. What is client-visible is a separate channel of cookie-sync hops and the wrapper operator's own requests [18].

The most useful result is a three-state one. `ssp-rtb.sape.ru` asks for `places[].sapeFpUids[]`, an eids-shaped array of source/id pairs, on every bid request [19]; cold, 22 empty and 0 populated, warm, 20 empty and 2 populated on a single site with a 19-digit and a 32-hex id [20]. Two of 44 [21]. The author's reading is that the slot exists and this browser has nothing to put in it, a fact about the browser rather than the protocol [22], with `kimberlite.io` as the pure case: a stable id requested on all 18 of its requests, never once returned [23].

Separately, `mc.yandex.com` receives a `site-info` map with 295 key paths, mostly the publisher's own UI taxonomy, including publisher-declared visitor labels written in plain Russian as key names, 14 filled audience slots and one always empty [24]. The author is explicit that this is the analytics endpoint, not a bid request, and that nothing measured shows those labels reaching an auction [25].

Worth watching: whether the reworked schema carries an explicit truncation flag rather than a silent slice, and whether anyone reproduces the empty-slot state instead of rounding it to "carries identity" or "asks nothing" [26].

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories