I replayed every tracking URL stored from the public vastlint tester and inspector between August 27 and November 11, 2026. That is 13,466 URLs pulled off VAST tags people submitted, checked again with Pixellint 0.58.0 at each URL's original capture time. 165 URLs carried an error, 173 error findings in all. 116 of those findings are the same check: gdpr is present, and the value is not 0 or 1.
The same store also holds the Pixellint playground: 153 raw URLs, 65 JSON bodies, and 5 request captures. Those 223 pastes produced 250 errors, and they are hand-edited examples rather than tags from a player. They are not in the rates below. Four extra VAST URLs from the playground were clean and do not change the error count.
This is a recent sample of tags people chose to test. It is not a census of live ad traffic, and the check does not send the URL to the vendor. A clean result here does not mean the collector accepted, attributed, or billed the call.
Empty and macro GDPR flags are templates
TCF v2 says gdpr=0 when GDPR does not apply and gdpr=1 when it does. An empty value, or a macro the ad server has not expanded, is a template. It is not a claim that GDPR does or does not apply. Consent on the wire and TCF v2 on pixels are the longer form of that rule. Consent strings in this store are replaced with a sentinel before they are kept, and that sentinel is not scored as a bad TC String.
On the VAST URLs, the common values that did not fail were an empty gdpr (374), ${GDPR} (327), gdpr=0 (114), and bracket or brace macros such as [AV_GDPR] (36) and {{IS_GDPR}} (12). gdpr=1 showed up 7 times. Those are either a real flag or a token an ad server still has to fill.
116 errors are a flag the request will send literally
The failures are values the macro scanner does not treat as an unexpanded macro, so the GDPR check reads them as the flag:
| Value | VAST URLs | What is wrong with it |
|---|---|---|
!!GDPR! |
53 | One bang short of !!GDPR!!, so the token is sent as the flag |
NaN |
30 | A missing number that JavaScript stringified |
__ADFGDPR__ |
15 | A vendor token with no macro delimiter |
false |
8 | A boolean where the flag is 0 or 1
|
FT_GDPR |
7 | A name with no delimiter |
!!GDPR_ENFORCED! |
3 | The same unclosed bang form |
One more gdpr=true was a playground URL, not a VAST tracker. That brings the flag finding to 117 in the whole store.
!!GDPR! is also a broken-delimiter warning. Close it as !!GDPR!! and it is a template the ad server can expand. Leave the last bang off and the request sends the characters !!GDPR!, which is not 0 or 1. FT_GDPR and __ADFGDPR__ have the same problem without any delimiter at all: the name goes out as the value. NaN is what you get when a script interpolates an undefined number into the query. false and true are booleans from a JSON-minded template. The wire wants the digits. The field contract is GDPR flag literals.
Macros are the warnings, and a broken one becomes the error
The VAST URLs carried 1,106 warnings. 569 are a bracket token outside the IAB VAST macro table: players substitute the published names, and a vendor token stays in the request until that vendor expands it. 175 are broken delimiters, across 83 URLs. 13 use a lowercase VAST macro name. Unknown brackets, lowercase names, and a bang macro short one ! are VAST bracket macros. 137 still send the deprecated us_privacy string, which IAB Tech Lab replaced with GPP on January 31, 2024. 101 use http://, which is plain http on a tracking URL. 41 mix macro syntaxes in one URL. 37 repeat the GDPR consent signal on more than one parameter, across 19 URLs (duplicate consent signals).
The split that matters is template versus fired. A stored tag may legally contain ${GDPR} or !!GDPR!!. A request the player already fired may not. Template and fired URLs is the rule those warnings are applying. The same macros sit on impression, quartile, and click URLs, because those tracking events are pixels: VAST tracking events are pixels.
Vendor parameters that repeat are a short list
After the flag, the VAST error findings that show up more than twice are small:
| Check | Findings |
|---|---|
IAS video xsId empty |
24 |
IAS video xsId still a placeholder |
12 |
Comscore c1 invalid |
4 |
Comscore c7 missing |
4 |
Amazon video framework vstevt missing |
5 |
That is 36 IAS findings and 13 other vendor findings, beside the 116 flag findings. Eight further findings sit outside that list. Empty xsId, the installation placeholder, and a cachebuster used as the session id are IAS video xsId. A vendor pack ran on 7,861 of the 13,693 stored artifacts in the whole store. Campaign Manager 360 VAST events and Google IMA interaction account for most of those. The endpoint check is running. It is not where the repeated failures are.
12,488 of the 13,470 stored VAST URLs had neither an error nor a warning.
What to check on the next tag
If the ticket is a wrapper URL, walk the hops in the inspector. If you have the tag and want the tracking nodes, use the tester. For one pixel, the same flag check is local:
$ pixellint validate url 'https://example.com/track?gdpr=!!GDPR!'
An implementation that lets a model emit the pixel still has to check the flag and the macro before the URL is trafficked. Pixellint is that check for pixels, postbacks, and the tracking URLs on a VAST hop. It is independent of IAB Tech Lab, and the spec does not require it. Paste a URL in the playground, or install the CLI with cargo install pixellint or npm install pixellint.
Send 0 or 1, or a closed macro the ad server will expand. A vendor bracket in a field the player will not substitute stays in the request. The same counts, with the sample limits, are on the Pixellint blog: Submitted VAST tracking URLs fail the GDPR flag more often than the vendor contract.
Top comments (0)