Pixels · engineers · marketers
The fired URL still says CACHEBUSTER
Pixellint core treats [NAME], ${NAME}, and {{NAME}} as macros. Mixed syntax in one URL is a trafficking smell. An unexpanded macro on a fired artifact is a hard miss. A template in GAM is supposed to still contain the token. Tell the validator which one you pasted.
Three syntaxes
Square brackets are GAM and many VAST insertions: [CACHEBUSTER], [TIMESTAMP], [CLICK_URL]. Dollar-braces are another family: ${CLICK_URL}, ${GDPR_CONSENT_1234}. Double braces are a third: {{CACHEBUSTER}}, {{PLAYER_WIDTH}}. Pixellint recognizes those three. Per-vendor vocabularies (which names are legal) belong in vendor packs; core only knows the shape.
One artifact, one syntax. Mixing [CACHEBUSTER] and ${RANDOM} means two systems were supposed to expand the same URL and probably only one did. core.macro.mixed_syntax flags that. Do not wrap the same value two ways to be thorough. Pick the syntax the ad server that serves this creative actually expands.
Unsafe position
A macro in the hostname or scheme does not expand into a valid URL, it expands into garbage. https://${HOST}/pixel or [PROTOCOL]://ad.example/imp will not become a legal collector after substitution, or it will become a collector you did not intend. Macros never belong in scheme, authority, host, port, or userinfo. core.macro.unsafe_position is that rule.
Put macros in query or path slots the ad server documents. ord=[timestamp] on a Floodlight path is a documented slot. Cache busters in the query are a documented slot. A click URL as a query value (${CLICK_URL} encoded) is a documented slot. A macro as the host is a creative that will 404 after serve, or worse, send data to a substituted host you do not control.
Unexpanded on fired, legal on template
A tag sitting in GAM is a template. Macros are supposed to be there. A HAR from a real impression is fired. Fired URLs must not contain unexpanded macros (core.macro.unexpanded_in_fired_url). Unknown state stays conservative. Pass --state template or --state fired so the same paste is not both a pass and a fail depending on the day.
A live Floodlight hit that still has ord=[timestamp] never cache-busted and never uniquely counted. A live VAST Impression that still has [CACHEBUSTER] is the same miss. A live postback that still has {idfa} never identified a device. Expand at the system that knows the value: the ad server at serve time, the MMP at conversion time, not the vendor after receipt.
Consent macros are not the letter 1
IAB macros for TC strings and GPP have to expand to a real string, not to 1 or true. gdpr_consent=1 is valid base64 and still not a TC String. The first six bits of a TCF v2 string have to read 2. 36 characters is the floor for the core segment. Pixellint decodes; it does not sniff the alphabet. gdpr_consent=true fails the same way. gdpr must be 0 or 1; gdpr=1 needs the string.
A fired tag with ${GDPR_CONSENT_...} still in the query never carried consent. A fired tag with gdpr_consent=1 carried a placeholder that looks populated in a log. Empty values and unexpanded macros are template exemptions so a GAM creative is not punished for being a template. On a fired URL they are misses. Pass the CMP string through unmodified. Do not replace it with 1 because a ticket said the user consented.
Core URL still applies
After expansion the artifact must still be an absolute URL with a host, http or https, no userinfo. Fragments never reach the server. Plain http is flagged for upgrade. A macro that expands to an empty host trips core.url.host_missing. A macro that expands to javascript: trips unsupported scheme. Validate the fired form, not only the template, or you will ship a creative that is pretty in GAM and illegal on the wire.
The example below fails three ways if you mark it fired: mixed syntax, a macro in the host, and gdpr_consent=1 which decodes as base64 and is not a TC String. As a template it is still an unsafe host. Put the cache buster in the query, put a real host in the host, and put a CMP string in gdpr_consent when gdpr=1.
https://${HOST}/imp?cb=[CACHEBUSTER]&gdpr_consent=1
Check the artifact
Paste the pixel URL or JSON body into the
playground. Same engine as
pixellint validate. Nothing leaves the tab.