{"record":{"id":"2a2ca33e726d3c1f","repo":"AlistGo/alist","slug":"sub-playlist-not-found-in-master-m3u8","errorCode":null,"errorMessage":"sub playlist not found in master m3u8","messagePattern":"sub playlist not found in master m3u8","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/139/util.go","lineNumber":1598,"sourceCode":"\t\t\tif i+1 < len(lines) {\n\t\t\t\tsubRelPath = strings.TrimSpace(lines[i+1])\n\t\t\t\tif strings.Contains(line, \"1920x1080\") {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif subRelPath == \"\" {\n\t\tfor i := len(lines) - 1; i >= 0; i-- {\n\t\t\tline := strings.TrimSpace(lines[i])\n\t\t\tif line != \"\" && !strings.HasPrefix(line, \"#\") {\n\t\t\t\tsubRelPath = line\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif subRelPath == \"\" {\n\t\treturn \"\", fmt.Errorf(\"sub playlist not found in master m3u8\")\n\t}\n\n\t// 3. Get sub-playlist content\n\tbase, _ := url.Parse(masterURL)\n\tref, _ := url.Parse(subRelPath)\n\tsubURL := base.ResolveReference(ref).String()\n\n\tresp, err = client.R().SetHeaders(headers).Get(subURL)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tsubContent := resp.String()\n\n\t// 4. Resolve relative TS paths to absolute URLs\n\tsubBase, _ := url.Parse(subURL)\n\tsubLines := strings.Split(subContent, \"\\n\")\n\tvar finalLines []string\n\tfor _, line := range subLines {","sourceCodeStart":1580,"sourceCodeEnd":1616,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/139/util.go#L1580-L1616","documentation":"While resolving an m3u8 video link, the driver parsed the master playlist (looking for a #EXT-X-STREAM-INF line followed by a URI, then falling back to the last non-comment, non-empty line) and found no candidate sub-playlist path at all. It cannot proceed to fetch the actual media segments.","triggerScenarios":"getLink for a video returns an m3u8 whose content is only tags/comments (e.g. a bare #EXTM3U, an error playlist, or a media playlist already), or the body is HTML that happens to pass earlier checks, leaving subRelPath empty.","commonSituations":"Upstream CDN serving an error/empty playlist when the video transcode is not ready, token-expired playlists, or format changes in the 139 video preview API.","solutions":["Retry after a short delay — transcode-not-ready playlists frequently become valid once processing finishes","Dump the master m3u8 content (log masterURL and body) to confirm whether it is a valid master playlist, a media playlist, or an error page","If it is already a media playlist (contains #EXTINF segments but no variants), resolve segment URLs directly instead of recursing; update OpenList or patch locally","Check the token/Referer headers still match what the CDN expects"],"exampleFix":"// before\nif subRelPath == \"\" {\n\treturn \"\", fmt.Errorf(\"sub playlist not found in master m3u8\")\n}\n// after (treat as media playlist when segments exist)\nif subRelPath == \"\" {\n\tif strings.Contains(masterContent, \"#EXTINF\") {\n\t\treturn masterURL, nil // already a media playlist\n\t}\n\treturn \"\", fmt.Errorf(\"sub playlist not found in master m3u8\")\n}","handlingStrategy":"fallback","validationCode":"// Validate the master playlist before searching for variants\nlines := strings.Split(masterContent, \"\\n\")\nif !containsTag(lines, \"#EXTM3U\") {\n\treturn fmt.Errorf(\"not a valid m3u8 playlist: %q\", truncate(masterContent, 64))\n}","typeGuard":"func isMasterPlaylist(content string) bool {\n\treturn strings.Contains(content, \"#EXT-X-STREAM-INF\")\n}","tryCatchPattern":"// Fall back to treating the URL as a direct media playlist\nurl, err := resolveSubPlaylist(master)\nif err != nil && strings.Contains(err.Error(), \"sub playlist not found\") {\n\tif strings.Contains(master.content, \"#EXTINF\") {\n\t\treturn masterURL, nil\n\t}\n\treturn \"\", err\n}","preventionTips":["Retry m3u8 fetches shortly after upload — transcoding playlists appear late","Always check for #EXTM3U before parsing to distinguish playlists from error pages","Cache resolved sub-playlist URLs briefly to avoid re-fetching masters"],"tags":["m3u8","video","parsing","streaming"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}