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
- Print/log the beginning of M3u8Content to see what was actually fetched (usually an HTML error page).
- Fix the playlist URL — it must return a real HLS manifest starting with '#EXTM3U'.
- Add required headers/cookies (Referer, User-Agent) via ParserConfig so the server stops returning an error page.
- If the content is DASH, use the DASH extractor instead of the HLS one.
- 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
- Check the response's first line for '#EXTM3U' before extraction.
- Send proper User-Agent/Referer/cookie headers so servers don't return HTML error pages.
- Use the DASH extractor for .mpd URLs, HLS extractor only for HLS manifests.
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
- Bad vtt!
- ResString.keyProcessorNotFound
- PSSH version can only be 0 or 1
- Mp4 box names must be 4 characters long
- MDHD version can only be 0 or 1
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)