subhra74/xdm · error · Exception

Unable to download HLS manifest

Error message

Unable to download HLS manifest

What it means

The fallback branch of MultiSourceHLSDownloader.ProbeTarget: when the HLS manifest download failed but FindErrorStatus() found no specific HTTP status code, XDM throws 'Unable to download HLS manifest'. This indicates a failure to fetch the manifest without an identifiable HTTP error response (e.g. connectivity loss or an exception without a captured status).

Solutions

  1. Check basic network connectivity to the manifest host (curl the m3u8 URL to reproduce)
  2. Verify the m3u8 URL is current; re-capture it from the page if the download is old
  3. Check proxy/firewall/TLS interception that can break the request without a clean HTTP status
  4. Retry the download; transient network failures surface as this unspecific error

Example fix

// before
throw new Exception("Unable to download HLS manifest");
// after
Log.Warn("HLS manifest fetch failed without an HTTP status; likely network error");
throw new DownloadException(ErrorCode.NetworkFailure,
    "Unable to download HLS manifest (no HTTP status captured; check connectivity)");
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the manifest is reachable before calling ProbeTarget
var probe = await http.SendAsync(new HttpRequestMessage(HttpMethod.Head, manifestUrl));
if (!probe.IsSuccessStatusCode)
    throw new InvalidOperationException($"Manifest unreachable: {(int)probe.StatusCode}");

Try / catch

try
{
    downloader.ProbeTarget();
}
catch (Exception ex) when (ex.Message == "Unable to download HLS manifest")
{
    Log.Warn("HLS manifest fetch failed without HTTP status; retrying after network check");
    await WaitForNetworkAsync();
    downloader.ProbeTarget();
}

Prevention

When it happens

Trigger: ProbeTarget's manifest download task completes with success=false and FindErrorStatus() returns null/HasValue==false, so no HTTP status is attributed to the failure.

Common situations: Network drop or DNS failure while fetching the m3u8; TLS errors; server closing the connection mid-response; proxy failures; manifest host unreachable.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at app/XDM/XDM.Core/Downloader/Adaptive/Hls/MultiSourceHLSDownloader.cs:191

                        if (status[1] != HttpStatusCode.OK) return status[1];
                        return null;
                    }
                    else
                    {
                        if (status[0] != HttpStatusCode.OK) return status[0];
                        return null;
                    }
                }

                if (!success)
                {
                    var statusCode = FindErrorStatus();
                    if (statusCode.HasValue)
                    {
                        throw new Exception($"Invalid response code: {statusCode.Value}",
                            new HttpException(statusCode.Value.ToString(), null, statusCode.Value));
                    }
                    throw new Exception("Unable to download HLS manifest");
                }

                var playlists = new Dictionary<string, HlsPlaylist>();
                if (state.Demuxed)
                {
                    playlists["video"] = HlsParser.ParseMediaSegments(results[0]!.Split('\n'), state.NonMuxedVideoPlaylistUrl.ToString());
                    playlists["audio"] = HlsParser.ParseMediaSegments(results[1]!.Split('\n'), state.NonMuxedAudioPlaylistUrl.ToString());

                    this._state.VideoContainerFormat = GuessContainerFormatFromPlaylist(playlists["video"]);
                    this._state.AudioContainerFormat = GuessContainerFormatFromPlaylist(playlists["audio"]);
                    
                    var ext = FileExtensionHelper.GuessContainerFormatFromSegmentExtension(
                            this._state.VideoContainerFormat, this._state.AudioContainerFormat);
                    TargetFileName = Path.GetFileNameWithoutExtension(TargetFileName ?? "video")
                            + ext;

                    Log.Debug($"Guessed Demuxed formats - VideoContainerFormat: {this._state.VideoContainerFormat} AudioContainerFormat: {this._state.AudioContainerFormat}");
                    Log.Debug($"Guessed - ext: {ext} TargetFileName: {TargetFileName}");

View on GitHub (pinned to 1ca5a25aae)