{"record":{"id":"8f6ae732e46a4ad2","repo":"iawia002/lux","slug":"invalid-url-format","errorCode":null,"errorMessage":"invalid URL format","messagePattern":"invalid URL format","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extractors/threads/threads.go","lineNumber":56,"sourceCode":"\t\t},\n\t}\n}\n\ntype media struct {\n\tURL  string\n\tType extractors.DataType\n}\n\n// Extract is the main function to extract the data.\nfunc (e *extractor) Extract(url string, option extractors.Options) ([]*extractors.Data, error) {\n\tURL, err := netURL.Parse(url)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tpaths := strings.Split(URL.Path, \"/\")\n\tif len(paths) < 3 {\n\t\treturn nil, errors.New(\"invalid URL format\")\n\t}\n\n\tposter := paths[1]\n\tshortCode := paths[3]\n\n\tmedias := make([]media, 0)\n\n\ttitle := fmt.Sprintf(\"Threads %s - %s\", poster, shortCode)\n\n\tcollector := colly.NewCollector()\n\tcollector.SetClient(e.client)\n\n\t// case single image or video\n\tcollector.OnHTML(\"div.SingleInnerMediaContainer\", func(e *colly.HTMLElement) {\n\t\tif src := e.ChildAttr(\"img\", \"src\"); src != \"\" {\n\t\t\tmedias = append(medias, media{\n\t\t\t\tURL:  src,\n\t\t\t\tType: extractors.DataTypeImage,","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/iawia002/lux/blob/dd00f6d258d80b6684a0b9402d7124e5c18ef42f/extractors/threads/threads.go#L38-L74","documentation":"The Threads extractor parses the URL path and requires a poster segment and a shortCode segment (it reads paths[1] and paths[3]). If splitting the path on '/' yields fewer than 3 parts, the URL does not look like a post permalink and this error is returned. Beware an off-by-one: the guard checks len(paths) < 3 but the code then reads paths[3], so a 3-part path (e.g. /@user/x) passes the guard and panics with index out of range instead.","triggerScenarios":"Calling Extract with a profile URL like https://www.threads.net/@user (path '/@user' → 2 parts → this error), or with a 3-part path like /@user/replies which slips past the guard and crashes on paths[3]. Only full permalinks /@user/post/SHORTCODE (4 parts) work.","commonSituations":"Users paste a profile or search URL instead of a post permalink; scripts iterate over mixed Threads URLs; truncated share links missing the final segment.","solutions":["Use the full post permalink form: https://www.threads.net/@user/post/SHORTCODE","Fix the guard in extractors/threads/threads.go to len(paths) < 4 so a 3-part path fails with this error instead of panicking","Validate the URL shape on the caller side before invoking Extract","Check for stray trailing slashes or query fragments that alter the path split"],"exampleFix":"// before\npaths := strings.Split(URL.Path, \"/\")\nif len(paths) < 3 {\n    return nil, errors.New(\"invalid URL format\")\n}\nposter := paths[1]\nshortCode := paths[3]\n\n// after\npaths := strings.Split(URL.Path, \"/\")\nif len(paths) < 4 {\n    return nil, errors.New(\"invalid URL format: expected /@user/post/shortCode\")\n}\nposter := paths[1]\nshortCode := paths[3]","handlingStrategy":"validation","validationCode":"func isThreadsPostURL(raw string) bool {\n    u, err := url.Parse(raw)\n    if err != nil || u.Host != \"www.threads.net\" {\n        return false\n    }\n    parts := strings.Split(u.Path, \"/\")\n    return len(parts) >= 4 && strings.HasPrefix(parts[1], \"@\") // /@user/post/shortCode\n}","typeGuard":null,"tryCatchPattern":"if !isThreadsPostURL(url) {\n    return fmt.Errorf(\"not a threads post permalink: %s\", url)\n}\n_, err := threads.Extract(url, opts) // still keep error handling for runtime failures","preventionTips":["Only feed full /@user/post/SHORTCODE permalinks to the extractor","Caller-side path-segment validation avoids both the error and the 3-part-path panic","Reject profile/search URLs before batch extraction"],"tags":["threads","url-validation","index-out-of-range","off-by-one"],"backgroundTag":null,"analyzedSha":"dd00f6d258d80b6684a0b9402d7124e5c18ef42f","analyzedAt":"2026-08-15T16:57:31.080Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}