VASTlint
Back to blog
Standards/6 min read

An Empty VAST Wrapper Is Schema-Valid in 4.4. It Was Not in 2.0.

One compositor change in the VAST 4.4 draft schema makes AdSystem, Impression and VASTAdTagURI optional on every wrapper, lets singular elements repeat, and drops element ordering. Here is the reproduction and what it means if you gate on the XSD.

Author

Alex Sekowski

Published

August 9, 2026

Reading time

6 min read

VAST 4.4XSDWrapperValidationIAB Tech Lab

A VAST wrapper with no AdSystem, no VASTAdTagURI and no Impression validates against the VAST 4.4 draft schema. The same document has been invalid in every version from 2.0 through 4.2. It is one line of XSD, and it is almost certainly a side effect of the CTV Ad Portfolio restructure rather than a decision anyone made on purpose.

I have filed it with IAB Tech Lab. This post is the working, because the reproduction is short enough that anyone can check it in about a minute.

The change

In vast_4.4.xsd on master, both vastInLine_type and vastWrapper_type wrap their children in a single compositor: an xs:choice with minOccurs zero and maxOccurs unbounded.

That looks harmless. It is the idiom people reach for when they want to say "these children may appear in any order". What it actually says is stronger than that. In XSD, the cardinality on the compositor governs the content model, and the minOccurs on the individual child elements only describes a single selection from the choice. Set the choice itself to zero-or-more and every constraint underneath it stops binding.

So the children still declare minOccurs="1". They are still, in effect, optional.

The compositor in question
xml
<!-- vast_4.4.xsd, vastWrapper_type and vastInLine_type --><xs:choice minOccurs="0" maxOccurs="unbounded">  <xs:element name="AdSystem"     type="vastAdSystem_type"/>  <xs:element name="VASTAdTagURI" type="vastURIElement_type"/>  <xs:element name="Impression"   type="vastImpression_type"/>  <xs:element name="Creatives"    type="vastCreatives_type"/>  <!-- ... --></xs:choice>

Three consequences, not one

The empty wrapper is the headline, but the compositor gives up three separate guarantees at once. Each is reproducible with xmllint against the published schema.

What now validates in 4.4

  • Everything is optional. An empty <Wrapper/> validates. So does an empty <InLine/>, with no AdSystem, no AdTitle, no Impression and no Creatives.
  • Everything repeats. maxOccurs="unbounded" on the choice means any branch can be selected repeatedly, so three <AdSystem> elements in one InLine validate, even though AdSystem has always been exactly one.
  • Order stops mattering. 4.2 used xs:sequence, so the order was fixed. A choice imposes none, so <Impression> before <AdSystem> validates.
Reproduction: all three validate against the published 4.4 schema
xml
<!-- 1. empty wrapper --><VAST version="4.4" xmlns="http://www.iab.com/VAST">  <Ad id="a"><Wrapper/></Ad></VAST> <!-- 2. empty inline --><VAST version="4.4" xmlns="http://www.iab.com/VAST">  <Ad id="a"><InLine/></Ad></VAST> <!-- 3. AdSystem three times --><VAST version="4.4" xmlns="http://www.iab.com/VAST">  <Ad id="a">    <InLine>      <AdSystem>A</AdSystem>      <AdSystem>B</AdSystem>      <AdSystem>C</AdSystem>    </InLine>  </Ad></VAST>
Check it yourself
bash
curl -sO https://raw.githubusercontent.com/InteractiveAdvertisingBureau/vast/master/vast_4.4.xsdxmllint --noout --schema vast_4.4.xsd empty-wrapper.xml# empty-wrapper.xml validates # the same shape against 4.2xmllint --noout --schema vast_4.2.xsd empty-wrapper-42.xml# Element 'Wrapper': Missing child element(s). Expected is ( AdSystem ).

Get VAST spec updates, platform guides, and release notes in your inbox.

What 4.2 actually required

The contrast is worth being precise about, because the requirement did not live where you might expect. In 4.2, Wrapper_type extends AdDefinitionBase_type, and it is the base type that carries the required fields: AdSystem exactly once, Impression once or more. Wrapper_type then adds VASTAdTagURI as required and Creatives as optional. Both use xs:sequence.

Impression in particular has been required on wrappers since 2.0. That is a twenty-year-old constraint, and the wrapper is exactly where it matters most, because a wrapper that fires no impression is a measurement hole in the middle of a chain.

The schema went from expressing a content model to enumerating a vocabulary. Those are different jobs, and only one of them can gate anything.

vastlint standards note

Why this matters if you gate on the XSD

Plenty of teams treat the published XSD as an acceptance gate: ad server ingest checks, partner certification, QA rigs that reject anything the schema will not accept. For those, 4.4 currently passes wrapper responses that cannot deliver an ad and cannot record an impression.

It also sharpens a question the working group was already asking in a separate thread about whether the XSD is meant to be usable as a validator at all. My answer has not changed: it is not, and it never fully was. The prose has always carried requirements the schema does not express, which is the reason a rule-based validator exists in the first place. But there is a difference between a schema that is incomplete and a schema that is more permissive than the version it supersedes.

The practical takeaway for anyone shipping 4.4 today: do not loosen your own checks to match the draft. Keep requiring AdSystem, Impression and VASTAdTagURI on wrappers, because the prose still does and every prior version did.

The likely fix

If the intent was order-independence, xs:all expresses that while keeping per-element cardinality intact. XSD 1.0 limits xs:all particles to maxOccurs one, so repeating elements like Impression need handling, but the required ones stay required.

If the intent really was a fully open content model, then the schema and the prose have diverged and the annotation should say so out loud, so that nobody builds a gate on top of it expecting otherwise.

Either way it is a small patch. I have offered to write it.

Validate a 4.4 tag against the prose, not just the schema

vastlint checks VAST 2.0 through the 4.4 draft against rules derived from the specification text, not only the XSD, so a wrapper missing AdSystem or Impression still fails. Paste a tag and see. Nothing is stored.

Open the VAST validator

Sources and further reading

The filed report, with the full reproduction and the 4.2 comparison table.

The published draft schema. See vastInLine_type and vastWrapper_type.

The full technical breakdown of the 4.4 draft, content model by content model.

Element-by-element changes with a migration checklist.

What a wrapper has to carry for a chain to resolve and measure correctly.

Keep reading

Related stories

All posts