nilaoda/N_m3u8DL-RE · error · Exception

ResString.badM3u8

Error message

ResString.badM3u8

What it means

HLSExtractor.PreProcessContent validates that the downloaded playlist text starts with the '#EXTM3U' tag (HLSTags.ext_m3u) and throws the localized ResString.badM3u8 message if it does not. This guards the extractor from parsing HTML error pages, JSON API responses, or other non-playlist content as an M3U8.

Solutions

  1. Print/log the beginning of M3u8Content to see what was actually fetched (usually an HTML error page).
  2. Fix the playlist URL — it must return a real HLS manifest starting with '#EXTM3U'.
  3. Add required headers/cookies (Referer, User-Agent) via ParserConfig so the server stops returning an error page.
  4. If the content is DASH, use the DASH extractor instead of the HLS one.
  5. Catch the error and show the localized ResString.badM3u8 message to the user.

Example fix

// before
var extractor = new HLSExtractor(parserConfig);
var streams = await extractor.LoadM3u8FromUrlAsync(new Uri(url));

// after
parserConfig.Headers["User-Agent"] = "Mozilla/5.0 ...";
parserConfig.Headers["Referer"] = pageUrl;
var content = await http.GetStringAsync(url);
if (!content.TrimStart().StartsWith("#EXTM3U"))
    throw new Exception($"URL did not return an M3U8 playlist: {content[..80]}...");
Defensive patterns

Strategy: validation

Validate before calling

var content = await DownloadPlaylistAsync(url);
if (!content.TrimStart().StartsWith("#EXTM3U"))
    throw new InvalidDataException($"Not an M3U8 playlist (got: {content[..Math.Min(80, content.Length)]}...)");

Type guard

static bool LooksLikeM3u8(string content) =>
    !string.IsNullOrWhiteSpace(content) && content.TrimStart().StartsWith("#EXTM3U");

Try / catch

try { var streams = await extractor.ExtractStreamsAsync(rawText); }
catch (Exception ex) when (ex.Message == ResString.badM3u8)
{ /* show user: URL did not return an HLS playlist */ }

Prevention

When it happens

Trigger: Constructing HLSExtractor and calling ExtractStreamsAsync/LoadM3u8FromUrlAsync with content whose trimmed first line is not '#EXTM3U'.

Common situations: The playlist URL points at a login/anti-bot HTML page, the server returns a 403/404 page with HTTP 200, the URL is a DASH .mpd or plain media segment rather than an HLS playlist, or content was double-decoded/encrypted.

Related errors


AI-assisted analysis of nilaoda/N_m3u8DL-RE@e113dee70c (2026-09-13). Data as JSON: /api/errors/cf659e0d6fe4fe8a. Report an issue: GitHub.

Appendix: source

Thrown at src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs:43

        this.ParserConfig = parserConfig;
        this.M3u8Url = parserConfig.Url ?? string.Empty;
        this.SetBaseUrl();
    }

    private void SetBaseUrl()
    {
        this.BaseUrl = !string.IsNullOrEmpty(ParserConfig.BaseUrl) ? ParserConfig.BaseUrl : this.M3u8Url;
    }

    /// <summary>
    /// 预处理m3u8内容
    /// </summary>
    public void PreProcessContent()
    {
        M3u8Content = M3u8Content.Trim();
        if (!M3u8Content.StartsWith(HLSTags.ext_m3u))
        {
            throw new Exception(ResString.badM3u8);
        }

        foreach (var p in ParserConfig.ContentProcessors)
        {
            if (p.CanProcess(ExtractorType, M3u8Content, ParserConfig))
            {
                M3u8Content = p.Process(M3u8Content, ParserConfig);
            }
        }
    }

    /// <summary>
    /// 预处理URL
    /// </summary>
    public string PreProcessUrl(string url)
    {
        foreach (var p in ParserConfig.UrlProcessors)
        {

View on GitHub (pinned to e113dee70c)