{"record":{"id":"f014121b8b4324bd","repo":"iawia002/lux","slug":"url-is-null","errorCode":null,"errorMessage":"url is null","messagePattern":"url is null","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"utils/utils.go","lineNumber":269,"sourceCode":"\t// has no suffix\n\tcontentType, err := request.ContentType(uri, uri)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\treturn filename[0], strings.Split(contentType, \"/\")[1], nil\n}\n\n// Md5 md5 hash\nfunc Md5(text string) string {\n\tsign := md5.New()\n\tsign.Write([]byte(text)) // nolint\n\treturn fmt.Sprintf(\"%x\", sign.Sum(nil))\n}\n\n// M3u8URLs get all urls from m3u8 url\nfunc M3u8URLs(uri string) ([]string, error) {\n\tif len(uri) == 0 {\n\t\treturn nil, errors.New(\"url is null\")\n\t}\n\n\thtml, err := request.Get(uri, \"\", nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tlines := strings.Split(html, \"\\n\")\n\tvar urls []string\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line != \"\" && !strings.HasPrefix(line, \"#\") {\n\t\t\tif strings.HasPrefix(line, \"http\") {\n\t\t\t\turls = append(urls, line)\n\t\t\t} else {\n\t\t\t\tbase, err := url.Parse(uri)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/iawia002/lux/blob/dd00f6d258d80b6684a0b9402d7124e5c18ef42f/utils/utils.go#L251-L287","documentation":"utils.M3u8URLs refuses to run when handed an empty string: len(uri) == 0 returns 'url is null'. This is an argument-validation error — the function itself is fine, the caller passed an empty playlist URL. The real bug is upstream: whatever produced the m3u8 URL (an extractor's stream parsing) came back empty.","triggerScenarios":"An extractor resolves a stream to an m3u8 playlist, the resolution yields \"\" (regex miss upstream), and M3u8URLs is called with it anyway. Typical in HLS-based extractors after the host page changed shape.","commonSituations":"Site markup changes making the playlist-URL regex capture empty; nil-vs-empty confusion after a failed MatchOneOf; refactors that drop the assignment of the URL.","solutions":["Check the uri argument for emptiness at the call site before invoking M3u8URLs and fail with a message naming the upstream extraction step","Trace back to where the m3u8 URL was produced and fix the extraction that returned \"\"","Add a nil/empty guard on the regex capture that builds the playlist URL","Write a unit test covering the empty-input path to lock in the behavior"],"exampleFix":"// before\nurls, err := utils.M3u8URLs(playlistURL) // playlistURL may be \"\"\nif err != nil { return err }\n\n// after\nif playlistURL == \"\" {\n    return errors.New(\"stream extraction produced an empty m3u8 playlist URL\")\n}\nurls, err := utils.M3u8URLs(playlistURL)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(playlistURL) == \"\" {\n    return nil, errors.New(\"refusing to fetch m3u8: playlist URL is empty (upstream extraction failed)\")\n}\nurls, err := utils.M3u8URLs(playlistURL)","typeGuard":null,"tryCatchPattern":"urls, err := utils.M3u8URLs(playlistURL)\nif err != nil {\n    if strings.Contains(err.Error(), \"url is null\") {\n        // argument bug on our side: find why playlistURL is empty, do not retry\n        return fmt.Errorf(\"m3u8 extraction upstream produced empty URL for %s\", pageURL)\n    }\n    return err\n}","preventionTips":["Always validate non-empty playlist URL before calling M3u8URLs","Nil-check regex captures that produce m3u8 URLs","Unit-test the empty-input path so regressions surface locally"],"tags":["m3u8","argument-validation","hls","empty-input"],"backgroundTag":null,"analyzedSha":"dd00f6d258d80b6684a0b9402d7124e5c18ef42f","analyzedAt":"2026-08-15T16:57:31.080Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}