---
title: "Diagnose video ad playback failures (VAST errors)"
description: "Reproduce a silent video failure with the placement's VAST tag, read what Epom actually returns on a no-ad, and map common player error codes to the Epom setting that caused them."
type: "troubleshooting"
audience: [network-manager]
keywords: [VAST, VAST error, video not playing, 303, no ad, wrapper, VPAID, media file, mime type, empty VAST, player, ad pod, skip, VAST version, viewability, Open Measurement, SSAI, stitcher, video requirements, in-stream type, error 900, undefined error, ERRORCODE, IMA error]
ui_path: "Account → Publishers → site → zone → placement (Video) → Ad tags & API → VAST tags"
---

Fetch the complete documentation index at: https://epom.com/ad-server/docs/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Diagnose video ad playback failures (VAST errors)

A video slot stays blank, the player skips straight to content, or you get a bare "ad error"
with no detail. Video players **swallow VAST errors** — they rarely tell you *why*, they just
move on. This page turns that silence into a diagnosis: reproduce the failure with the
placement's own VAST tag, read what Epom actually returned, and map the player's error code (if
you can get one) to the Epom setting behind it.

This is a video-specific companion to
[Tags and preview problems](https://epom.com/ad-server/docs/troubleshooting/tags-and-preview.md), which has the one-line version of this
symptom.

## First: what Epom returns when nothing serves

Knowing Epom's actual responses stops you chasing a player bug that is really a "no ad matched".
On a video (VAST) request Epom returns one of:

| Situation | Epom returns | What the player does |
|---|---|---|
| A banner matched | A full `<VAST>` with the creative's media/wrapper | Plays the ad |
| A request reached a real placement but **no banner matched** | A `<VAST>` with an **`<Error>`** element carrying a tracking **beacon URL** — Epom puts no error code of its own in it, only an `[ERRORCODE]` slot for the player to fill | Fires the beacon, then reports **its own** no-ads error (commonly **303**) |
| The request had **no valid placement** (bad key, wrong URL) | An **empty** `<VAST … />` with no `<Ad>`, at the version the tag asked for (2.0 when the request itself could not be read) | Reports an empty/parse error |

The key takeaway: **the error code you see in the player is the player's, not Epom's.** Epom's
no-ad response is a valid VAST document whose `<Error>` fires a beacon so the no-fill is counted;
the player then decides how to surface it. So a "303 No Ads" almost always means *Epom found the
placement but had nothing to serve* — a selection problem, not a tag problem. Work
[My ad is not serving](https://epom.com/ad-server/docs/troubleshooting/ad-not-serving.md) in that case.

## Step 1 — reproduce with the placement’s VAST tag

Don't debug on the live player first. Get a clean signal from the tag itself.

1. Open the video placement → the **Ad tags & API** card → the **VAST tags** tab. If your network
   has more than one ad-serving domain, pick the **Domain** on the card's **Settings** tab first.
2. Pick your player's spec in the **VAST version** picker (2.0, 3.0, 4.0, 4.1, 4.2 or 4.3) and
   copy the **VAST tag** URL — or copy the **VPAID tag** or **VMAP tag** URL, which do not follow
   the picker. The version rides in the tag's `vastv` parameter where it is visible — on networks
   that serve encrypted tag URLs it is inside the ciphertext, so read it off the response
   instead (below).
3. Paste it into a VAST validator/inspector (your player vendor's, or any standalone VAST
   tester). This shows the raw XML Epom returns for that exact tag, with none of the player's
   error-swallowing.

   The [AI assistant](https://epom.com/ad-server/docs/guides/collaboration/ai-assistant.md) can do this for you: ask it to
   check the tag URL and it summarises the response — VAST version, each ad as InLine or Wrapper
   (following wrappers up to three levels), media files with type, size, bitrate and VPAID, and
   tracking counts — or says there was **no ad**. Its request comes from a datacenter with a
   stock desktop, iPhone or Android browser, so a no-ad there can simply be targeting (country,
   device, IP, frequency) keeping that request out; confirm from a real device before blaming
   the tag.

What you learn immediately:

- **Empty `<VAST/>` or a `<VAST>…<Error>` with only a beacon** → no ad matched (or no valid
  placement). Go to [Step 2](#step-2-no-ad-matched-selection-side).
- **A full `<VAST>` with a `MediaFile` or `VASTAdTagURI`** → Epom is serving; the failure is
  **playback-side** (media, wrapper, VPAID, mime). Go to [Step 3](#step-3-epom-served-but-playback-fails).

> **Corner case: check which VAST version the response actually declares.** Read it off the root
> element — `<VAST version="…">` — rather than off the tag URL: the URL's `vastv=` parameter says
> the same thing, but on networks that serve encrypted tag URLs the whole query is wrapped into
> ciphertext and no `vastv` is visible even though the tag carries one. A hand-edited or truncated
> URL that genuinely has no `vastv` (or an unknown one) is answered with **VAST 2.0** for a no-ad
> response, which can be rejected by a player that expects 4.x features. The picker defaults to
> **4.2**. If the tag also has to be
> [viewability-measured](https://epom.com/ad-server/docs/concepts/viewability-and-open-measurement.md), it must be 4.1 or
> higher **and** play in an Open Measurement–capable player — see
> [Video placements](https://epom.com/ad-server/docs/guides/inventory/placement-video.md).

## Step 2: no ad matched, selection side

The tag returns empty or an `<Error>`-only VAST. Epom reached the placement but had no creative.
Check, most common first:

1. **No linked, active video banner.** A video placement needs a linked banner of a video type
   (Local Video, External Video, Third Party VAST/VPAID Tag URL, VPAID Framework, Advanced Local
   Video(s)) that is active, with an active campaign in valid dates/budget. See
   [Video banners](https://epom.com/ad-server/docs/guides/advertising/banner-video.md).
2. **The banner's In-Stream video type is not what the request asks for.** The server keeps only
   banners whose **In-Stream video type** (Linear / Non-linear) matches the types the tag asks
   for — a plain VAST tag asks for both, linear first — and drops the rest, which the player sees
   as a no-ad. This is a selection-side cause, not a player one.
3. **An uploaded video misses the site's Video creative requirements.** The file stays linked but
   is not served on that site; the placement's linked-banners tree marks it **Not served**. See
   [Check video creatives against a publisher's spec](https://epom.com/ad-server/docs/guides/inventory/video-creative-requirements.md).
4. **Targeting / pre-targeting rejected the request.** Country, device, time-range and other
   rules apply to video exactly as to display. Reproduce with the standalone
   [preview's IP override](https://epom.com/ad-server/docs/troubleshooting/tags-and-preview.md) to test another geo. Behind a server-side ad
   insertion stitcher the rules see the viewer details the stitcher forwards; one that forwards
   none shows the rules its own data-centre address (see
   [Serving behind SSAI](https://epom.com/ad-server/docs/guides/inventory/placement-video.md#serving-behind-server-side-ad-insertion-ssai)).
5. **Capping or limits exhausted.** A frequency cap or a spent budget/impression limit removes
   the only candidate.
6. **RTB-only demand that didn't bid.** If the placement relies on programmatic video, an empty
   response is a no-bid, not a bug.
7. **A pod that comes back shorter than you asked for** can be **Competitive separation in a
   pod** passing over a creative that clashes with one already in the break — see
   [Keeping competitors apart in a pod](https://epom.com/ad-server/docs/guides/inventory/placement-video.md#keeping-competitors-apart-in-a-pod).
8. **The site, zone or placement is switched off.** A VAST tag of a paused placement — or of a
   placement whose site or zone is paused — answers with no ad even though the banner side is
   fine. Supply needs placement **and** zone **and** site on.
9. **A limit on the placement or site.** A *Total* limit that is reached switches the entity
   off, and it stays off after you raise the limit — switch it on again yourself (see
   [Limits](https://epom.com/ad-server/docs/guides/delivery/limits.md)).
10. **Site- or zone-level pretargeting.** A country rule on the **site** (or zone) applies to
   every placement under it — a "VAST returns an error everywhere except from one country"
   report is usually this. Check the site's targeting, not only the campaign's.

## Is an empty VAST normal? (fill rate)

Yes, for part of the traffic. Every request the placement cannot fill — no eligible banner, a
cap, a no-bid — is answered with the no-ad VAST above, and the player shows its no-ads error for
it. A placement filling 15% of its requests answers 85% of them empty by design. Read the share
from a General report grouped by placement: **Impressions ÷ Requests**. Only a fill of zero, or
a sudden drop against the same period before, points to a set-up problem — then work the list
above. See also [Low fill rate](https://epom.com/ad-server/docs/troubleshooting/low-fill-rate.md).

Confirm it is selection, not tag, in analytics: a **General report grouped by placement** shows
non-zero **Requests** with zero **Impressions** for a no-fill. Full checklist:
[My ad is not serving](https://epom.com/ad-server/docs/troubleshooting/ad-not-serving.md).

## Step 3: Epom served but playback fails

The validator shows real creative markup, yet the live player still fails. The cause is now in
the creative or the player, not selection:

- **Third-party VPAID tag in a non-VPAID player.** A VAST tag that contains a `JavaScriptResource`
  / VPAID script needs a **VPAID-capable player**. A plain VAST player renders nothing for it and
  usually reports a linearity or media error. Confirm the banner is a *Third Party VAST/VPAID Tag
  URL* type and that your player supports VPAID.
- **Wrapper that never resolves.** A VAST Tag banner is a **wrapper** — the player must follow
  `VASTAdTagURI` to the real ad. If the wrapped tag is slow, unreachable, or exceeds the player's
  wrapper-depth limit, the player times out. On the banner, the **VAST 4.0 Wrapper properties**
  (*Follow additional wrappers*, *Allow multiple ads*, *Fallback on no ads*) control this chain —
  see [Video banners](https://epom.com/ad-server/docs/guides/advertising/banner-video.md). The depth and time limits themselves
  are the **player's**, not settings on the banner.
- **Media-file mime/codec the player can't decode.** For Local/External/Advanced Video, the
  player needs a `MediaFile` whose `type` (mime) and codec it supports. An MP4-only player fed a
  WebM rendition, or a mismatched declared mime, fails silently. Check the `<MediaFile type="…">`
  in the validator against your player's supported formats.
- **Ad-pod expectations.** If the placement returns a pod (**Return (ads in pod) > 1**) but the
  player requested a single ad, or the pod exceeds **Max. pod duration**, playback can partially
  fail. Match the placement's pod settings to what the player asks for.
- **Skip settings the player enforces differently.** The placement's **Show skip ad button /
  Show after** and the banner's **Show skip button after** feed the VAST; a player with its own
  skip policy may override or conflict with them.

## The skip button (skipoffset) is missing from the VAST

Epom writes `skipoffset` on the `<Linear>` element only for creatives it serves **inline** —
Local Video, External Video, Advanced Local Videos and VPAID Framework banners — from the
banner's **Show skip button after (seconds)** — or, when the banner has none, the placement's skip settings — and never in a
**VAST 2.0** response, which has no such attribute. A **Third Party VAST/VPAID Tag URL** banner
is a wrapper: the VAST specification has no skip offset on a wrapper, so the field on such a
banner has no effect. The skip offset has to be in the partner's own inline VAST, and the
player then shows it.

## Pre-roll, mid-roll or post-roll — do I need different VAST tags?

No. A VAST tag does not know where in the content it plays — the **player** decides when to
call it (before, during or after the video). The same placement's tag can serve pre-roll and
mid-roll. If you want the breaks counted separately in reports, use a placement per break (or
pass a different channel in each call). A **VMAP** tag is the exception that describes breaks
itself; its **Max pre-rolls** is set on the video placement — see
[Video placements](https://epom.com/ad-server/docs/guides/inventory/placement-video.md).

## The ad plays as a separate video on the page, not inside my player

The publisher put the **Video Player** tag on the page. That tab is a ready-made demo player
that renders the placement on its own — it is not a pre-roll for an existing player. For your
own player, give it the **VAST tag** URL (Ad Tags & API → VAST Tags) as its ad tag. The display
banner code of another placement is not a VAST tag either; only the video placement's VAST tag
URL returns VAST.

## Clicks differ between two players on the same VAST tag

The player decides what is clickable: one player makes the whole video area a link, another
shows only a call-to-action button. Epom counts a click when the player opens the VAST's
`ClickThrough` link (or fires its `ClickTracking`), so the same tag reports more clicks in the
player with the bigger click area. There is no `ClickThrough` at all when the video banner has
no landing URL.

## Reading player error codes (Epom-scoped)

Players report IAB VAST error codes. You don't need the whole dictionary — these are the ones
that map to an Epom-side setting or state:

| Player code (family) | Usually means | Where to look in Epom |
|---|---|---|
| **303** No ads after wrapper(s) | Epom returned a no-ad `<Error>` VAST | Selection — [Step 2](#step-2-no-ad-matched-selection-side) |
| **300–302** Wrapper error, timeout or depth limit | A VAST Tag banner's wrapped tag is slow or unreachable | Banner **VAST 4.0 Wrapper properties**, the wrapped tag itself |
| **901** VPAID error / unsupported | VPAID creative in a non-VPAID player | Banner type (VAST/VPAID Tag or VPAID Framework), player capability |
| **400–405** Media-file error | No playable `MediaFile`, or a codec/mime the player cannot decode | Banner media files, mime types |
| **201** Video player expecting different linearity | The creative is linear and the player wants non-linear, or the reverse | Banner **In-Stream video type** (Linear / Non-linear) |
| **100** XML parse error | Empty or malformed response reaching the player | Usually the **empty `<VAST/>`** case — bad placement key or hand-edited tag |
| **900** Undefined error | The player failed and had no more specific code for it | Work the checklist in [What does VAST error 900 mean?](#what-does-vast-error-900-undefined-error-mean) |

> **Note:** these ranges are the standard IAB families, but the exact code a player emits varies
> by player. Treat the code as a **pointer** to the Epom area, then confirm from the raw XML in
> Step 1 — the XML is authoritative, the player's code is a hint.

## What does VAST error 900 (Undefined error) mean?

**900 is the IAB VAST specification's "Undefined Error"** — the code a video **player** reports
when playback failed and it cannot name a more specific reason. Epom never produces it: every
`<Error>` URL Epom writes (on a served ad, on a wrapper and on the no-ad response) ends in
`ec=[ERRORCODE]`, and it is the player that replaces `[ERRORCODE]` with 900 or any other code.
Google IMA in particular folds many different failures into 900 — a VPAID creative that fails,
a media file it cannot use, a timeout, a request the browser blocked. So a 900 says "the player
gave up", not where; it needs the same diagnosis as any silent failure.

Check, in this order:

1. **Does Epom serve at all?** Open the placement's VAST tag in the browser (Step 1). An `<Error>`
   with no `<Ad>` is a no-fill — work [Step 2](#step-2-no-ad-matched-selection-side), the 900 is
   a consequence.
2. **Does the media file open, and does it suit the player?** Copy the `<MediaFile>` URL into a
   browser tab — it must play over `https`. Its `type` must be a format the player decodes (an
   MP4-only player cannot use WebM), and its size and bitrate must be reasonable for the
   connection (see [How heavy should a banner or video be?](https://epom.com/ad-server/docs/reference/creative-ad-specs.md#how-heavy-should-a-banner-or-video-be-recommended-weights-vs-hard-limits)).
3. **Is it VPAID?** A `MediaFile` of type `application/javascript` (or a VPAID Framework banner)
   needs a player with VPAID enabled; Google IMA needs VPAID mode switched on by the publisher.
   A VPAID script that throws inside the player is also reported as a generic failure.
4. **How deep is the wrapper chain?** A Third Party VAST/VPAID Tag URL banner is a wrapper; if the
   partner's tag wraps again, every hop costs time and each player stops after its own fixed
   number of hops. Open the partner's tag yourself and follow it to the inline ad.
5. **Is anything blocked by the browser?** Mixed `http` content on an `https` page, and a media
   file or VAST hosted on a server that does not allow cross-origin requests (CORS), fail in
   the player with no detail. Open the browser console and the Network tab on the publisher's
   page, play the ad, and look for red requests and CORS errors.
6. **What does the player itself say?** IMA, JW Player and Video.js write the underlying error
   to the console in debug mode; that message is more precise than the 900 the VAST pixel gets.

> **Where Epom sees the code:** when a player fires the `<Error>` pixel, the code it filled in is
> written to the ad server's troubleshooting log ("Player reported VAST error code 900") while a
> capture is running — your network administrator creates a time-boxed task under **Admin → Audit & diagnostics →
> Troubleshooting log tasks** and downloads the logs afterwards (see
> [Capture ad requests with a troubleshooting log task](https://epom.com/ad-server/docs/guides/admin/system-ops.md#capture-ad-requests-with-a-troubleshooting-log-task)).
> The code is not a column in the reports.

## Did the impression fire?

When a video ad does play, verify tracking is landing rather than assuming it from the visible
ad:

- The placement's **VAST tracking** card lets you enable playback events (start, quartiles,
  complete, skip, viewability). Enabled events appear in the VAST response as tracking URLs and
  are recorded when the player fires them — see [Video placements](https://epom.com/ad-server/docs/guides/inventory/placement-video.md).
- In analytics, compare **Requests → Impressions** first. Requests without impressions =
  no-fill (Step 2). For playback, group a General report by **Conversion action** (or **Attribution
  type**): the quartile, complete and viewability events are recorded as *system* conversions,
  which no longer count in the **Conversions** column. Impressions without start/quartile
  events = the player loaded the ad but playback didn't progress (Step 3 territory — media or
  player issue).
- **A ticked event can be missing from the XML by design.** Event names differ by VAST version
  and Epom leaves out what a version does not define: *Ad loaded* appears only from 4.1, *Exit
  fullscreen* from 3.0, and the linear-video *Expand* / *Collapse* are omitted from 4.x (see
  [Video banners](https://epom.com/ad-server/docs/guides/advertising/banner-video.md#what-a-server-side-ad-insertion-stitcher-receives)).
  If an enabled event is absent, check the version of the tag first.
- **OpenRTB video banners never get Epom's VAST tracking card** — their events come from the
  bid's own markup, so absent Epom-side events there is expected, not a bug (see
  [OpenRTB (DSP) banners](https://epom.com/ad-server/docs/guides/advertising/banner-openrtb-dsp.md)).

## Corner cases and gotchas

> **Corner case:** Epom's no-ad response is a **valid VAST document**, not an HTTP error. The
> request returns `200 text/xml` with an `<Error>` beacon inside. A player that only checks the
> HTTP status will think the call "succeeded" and then fail on the empty ad — look at the body,
> not the status code.

> **Corner case:** in a **VAST 2.0** no-ad response Epom appends `&force_fail=true` to the beacon
> (inside `<Ad><InLine><Error>`, because 2.0 has no document-level `<Error>`). This is expected in
> the raw XML of a no-ad response — it is not evidence of a forced failure of your creative.

> **Corner case:** a wrapped **third-party VAST that itself returns no ad** produces a 303 at the
> player even though Epom served a perfectly valid wrapper. The chain failed downstream of Epom —
> test the wrapped tag URL directly to confirm.

## When you escalate

Give your network administrator: the **placement ID**, the **VAST tag URL** you tested and its
version, the **raw XML** the validator showed (or "empty"), the **player and its error code**,
and the **time window + reporting time zone**. Your network administrator can additionally check gates
you can't see (consent enforcement, bot and IP filtering, DSP creative-scan verdicts), and Epom
support the ad-server snapshot state.

## Related

- [Tags and preview problems](https://epom.com/ad-server/docs/troubleshooting/tags-and-preview.md)
- [Video placements](https://epom.com/ad-server/docs/guides/inventory/placement-video.md)
- [Video banners](https://epom.com/ad-server/docs/guides/advertising/banner-video.md)
- [Video viewability and Open Measurement (OMID)](https://epom.com/ad-server/docs/concepts/viewability-and-open-measurement.md)
  — the ad plays but viewability stays at zero
- [My ad is not serving](https://epom.com/ad-server/docs/troubleshooting/ad-not-serving.md)
- [OpenRTB (DSP) banners](https://epom.com/ad-server/docs/guides/advertising/banner-openrtb-dsp.md)