subhra74/xdm · error

Missing MPD start tag:

Error message

Missing MPD start tag: 

What it means

MpdParser.Parse loads the downloaded DASH manifest into an XmlDocument with namespaces disabled and requires the root element to be exactly "MPD". If the document's root element is missing or named anything else, it throws to indicate the downloaded content is not a DASH MPD manifest. This is a guard against feeding HTML error pages, HLS playlists, or other non-MPD content into the DASH parser.

Solutions

  1. Check the actual HTTP response body for the playlist URL (curl it) and confirm it starts with <MPD.
  2. Verify the URL is a DASH manifest (.mpd) and not an HLS playlist or a webpage; use the correct parser for m3u8 content.
  3. Ensure cookies/headers/Referer required by the site are passed so the server returns the real manifest instead of a block/error page.
  4. Handle the null/other root element before parsing by fetching and sniffing the content type yourself.

Example fix

// before
var media = MpdParser.Parse(manifestUrl, null);

// after
var body = new HttpClient().GetStringAsync(manifestUrl).Result;
if (!body.TrimStart().StartsWith("<MPD"))
    throw new Exception("URL does not point to a DASH MPD manifest");
var media = MpdParser.Parse(manifestUrl, null);
Defensive patterns

Strategy: validation

Validate before calling

var body = await http.GetStringAsync(manifestUrl);
if (!body.TrimStart().StartsWith("<MPD", StringComparison.OrdinalIgnoreCase))
    throw new Exception("Response is not a DASH MPD manifest");

Try / catch

try { var media = MpdParser.Parse(url, null); }
catch (Exception ex) when (ex.Message.StartsWith("Missing MPD start tag"))
{ Log.Warn("Not a DASH manifest: {0}", url); }

Prevention

When it happens

Trigger: Calling Parse (via the DASH media-parser entry point) with a playlistUrl whose response body does not have a top-level <MPD> element — e.g. the URL returned an HTML login/error page, a redirect to a block page, an HLS m3u8 playlist, or an empty/truncated response.

Common situations: Server returns 200 with an HTML captcha or geo-block page instead of the manifest; user pasted an HLS (.m3u8) URL into a DASH-only flow; CDN misconfiguration serves an error document with HTTP 200; the manifest URL expired and the server responds with an error page.

Related errors


AI-assisted analysis of subhra74/xdm@1ca5a25aae (2026-09-13). Data as JSON: /api/errors/db462edd6ef24c11. Report an issue: GitHub.

Appendix: source

Thrown at app/XDM/XDM.Core/MediaParser/Dash/MpdParser.cs:33

    public static class MpdParser
    {
        private static string trickModeUri = "http://dashif.org/guidelines/trickmode";
        private static readonly Regex TemplatePattern =
            new Regex(@"\$(RepresentationID|Time|Time%0(?<time_digits>[\d]+)(?<time_dx>[dx])?|Number|Number%0(?<num_digits>[\d]+)(?<num_dx>[dx])?|Bandwidth)\$");
        //@"\$(RepresentationID|Time|Time(%0([\d]+)([dx])?)?|Number|Number(%0([\d]+)([dx])?)?|Bandwidth)\$");
        public static IList<IList<KeyValuePair<Representation?, Representation?>>> Parse(
            string[] manifestLines,
            string playlistUrl)
        {
            using var memstream = new MemoryStream(Encoding.UTF8.GetBytes(string.Join("\n", manifestLines)));
            var xmldoc = new XmlDocument();
            xmldoc.Load(new XmlTextReader(memstream)
            {
                Namespaces = false
            });

            if (xmldoc.DocumentElement?.Name != "MPD")
                throw new Exception("Missing MPD start tag: " + xmldoc.DocumentElement.Name);
            if (xmldoc.DocumentElement.Attributes["type"]?.Value == "dynamic")
                throw new Exception("Manifest type dynamic is not supported");
            if (xmldoc.DocumentElement.SelectSingleNode("descendant::ContentProtection") != null)
                throw new Exception("Encrypted manifest");

            var mediaList = new List<IList<KeyValuePair<Representation?, Representation?>>>();

            var mediaPresentationDuration =
                DashUtil.ParseXsDuration(xmldoc.DocumentElement.Attributes["mediaPresentationDuration"]?.Value ?? "0");
            var baseUrl = new Uri(playlistUrl);
            var baseUrlNodeRoot = xmldoc.DocumentElement.SelectSingleNode("child::BaseURL");
            if (baseUrlNodeRoot != null)
            {
                baseUrl = UrlResolver.Resolve(baseUrl, baseUrlNodeRoot.InnerText);
            }

            var periods = xmldoc.DocumentElement.SelectNodes("child::Period");
            if (periods == null || periods.Count < 1) throw new Exception("No period found!");

View on GitHub (pinned to 1ca5a25aae)