yuliskov/SmartTube · error · IllegalStateException

Malformed mpd file: ${mpdContent}

Error message

Malformed mpd file:
${mpdContent}

What it means

getManifest(Uri, InputStream) parses already-fetched mpd bytes with StaticDashManifestParser; an IOException from the parser (malformed XML, empty or truncated body) is rethrown as IllegalStateException embedding the entire mpd content for diagnosis. When the 'mpd' is actually an HTML error page, the exception text makes that obvious — which is the point of including it.

Source

Thrown at common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/ExoMediaSourceFactory.java:260

    }

    private SabrManifest getSabrManifest(MediaItemFormatInfo formatInfo) {
        SabrManifestParser parser = new SabrManifestParser();
        return parser.parse(formatInfo);
    }

    private DashManifest getManifest(MediaItemFormatInfo formatInfo) {
        DashManifestParser2 parser = new DashManifestParser2();
        return parser.parse(formatInfo);
    }

    private DashManifest getManifest(Uri uri, InputStream mpdContent) {
        DashManifestParser parser = new StaticDashManifestParser();
        DashManifest result;
        try {
            result = parser.parse(uri, mpdContent);
        } catch (IOException e) {
            throw new IllegalStateException("Malformed mpd file:\n" + mpdContent, e);
        }
        return result;
    }

    private DashManifest getManifest(Uri uri, String mpdContent) {
        DashManifestParser parser = new StaticDashManifestParser();
        DashManifest result;
        try {
            result = parser.parse(uri, FileHelpers.toStream(mpdContent));
        } catch (IOException e) {
            throw new IllegalStateException("Malformed mpd file:\n" + mpdContent, e);
        }
        return result;
    }

    /**
     * Use OkHttp for networking
     */

View on GitHub (pinned to 3de8d90593)

Solutions

  1. Read the embedded content prefix in the exception message — '<MPD' vs '<html' distinguishes a parser bug from a bad response
  2. Re-fetch the format info / refresh the URL and rebuild the media source before retrying
  3. If the body is HTML, investigate middleboxes (proxy, VPN, DNS filtering) rather than the parser
  4. Treat it as a parser defect only when the content is well-formed mpd and still fails

Example fix

// before
DashManifest m = parser.parse(uri, mpdStream); // IOException -> IllegalStateException

// after
if (mpdContent == null || !mpdContent.trim().startsWith("<")) {
    throw new IllegalStateException("URL did not return a manifest (likely an error page)");
}
DashManifest m = parser.parse(uri, FileHelpers.toStream(mpdContent));
Defensive patterns

Strategy: validation

Validate before calling

String body = readFully(stream);
if (body == null || !body.trim().startsWith("<")) {
    // not a manifest: refresh the url / format info instead of parsing
    return refreshAndRetry();
}
DashManifest manifest = parser.parse(uri, FileHelpers.toStream(body));

Try / catch

try {
    return getManifest(uri, stream);
} catch (IllegalStateException e) {
    // message embeds the body: inspect prefix, then re-fetch manifest and retry once
}

Prevention

When it happens

Trigger: The fetched URL returned an HTML error/block page instead of DASH xml (expired signed URL, geo-block, proxy interference); the body was truncated mid-transfer; the InputStream is empty because the upstream fetch already failed.

Common situations: Expired stream URLs from YouTube-style backends; VPN/ISP-injected pages; ad-blocking or AFR proxy rewriting responses; retrying playback with stale cached format info.

Understand the failure class

Related errors


AI-assisted analysis of yuliskov/SmartTube@3de8d90593 (2026-08-22). Data as JSON: /api/errors/0271519999e9dcb5. Report an issue: GitHub.