{"record":{"id":"9ef4178096447be3","repo":"AlistGo/alist","slug":"http-status-code-d","errorCode":null,"errorMessage":"http status code %d","messagePattern":"http status code (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/offline_download/http/client.go","lineNumber":66,"sourceCode":"\nfunc (s SimpleHttp) Run(task *tool.DownloadTask) error {\n\tu := task.Url\n\t// parse url\n\t_u, err := url.Parse(u)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq, err := http.NewRequestWithContext(task.Ctx(), http.MethodGet, u, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tresp, err := s.client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode >= 400 {\n\t\treturn fmt.Errorf(\"http status code %d\", resp.StatusCode)\n\t}\n\t// If Path is empty, use Hostname; otherwise, filePath euqals TempDir which causes os.Create to fail\n\turlPath := _u.Path\n\tif urlPath == \"\" {\n\t\turlPath = strings.ReplaceAll(_u.Host, \".\", \"_\")\n\t}\n\tfilename := path.Base(urlPath)\n\tif n, err := parseFilenameFromContentDisposition(resp.Header.Get(\"Content-Disposition\")); err == nil {\n\t\tfilename = n\n\t}\n\t// save to temp dir\n\t_ = os.MkdirAll(task.TempDir, os.ModePerm)\n\tfilePath := filepath.Join(task.TempDir, filename)\n\tfile, err := os.Create(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/internal/offline_download/http/client.go#L48-L84","documentation":"The simple HTTP offline-download tool streams the URL with a GET and rejects any response with status >= 400. There is no retry, range resume, or header customization, so any server-side refusal surfaces as this bare status-code error. The body of the response (which usually explains the refusal) is discarded.","triggerScenarios":"Adding a URL that returns 403 (hotlink/Referer protection), 404 (dead link), 401 (auth required), or 5xx (server down) when the http tool issues its GET.","commonSituations":"CDN links that require a Referer or User-Agent the tool does not send; expired signed URLs; geo-blocked servers; mistyped URLs; servers that reject plain Go http-client fingerprints.","solutions":["Verify the URL with curl -I (or a browser) from the same network to see the exact status","If the server requires Referer/UA/cookies, use the aria2 tool with header options instead of the plain http tool","Re-copy a fresh signed link if the URL has an expiry signature","Fall back to downloading on a machine that is not blocked (geo/IP restrictions)"],"exampleFix":"// before: plain GET, no headers\nreq, _ := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)\n\n// after (when using aria2 instead): pass custom headers\n{ \"tool\": \"aria2\", \"url\": \"...\", \"headers\": [\"Referer: https://example.com\", \"User-Agent: Mozilla/5.0\"] }","handlingStrategy":"validation","validationCode":"req, _ := http.NewRequest(http.MethodHead, url, nil)\nresp, err := http.DefaultClient.Do(req)\nif err != nil || resp.StatusCode >= 400 {\n    // reject before queuing the download\n    return fmt.Errorf(\"url not downloadable (status %d)\", resp.StatusCode)\n}","typeGuard":null,"tryCatchPattern":"resp, err := s.client.Do(req)\nif err != nil {\n    return err\n}\ndefer resp.Body.Close()\nif resp.StatusCode >= 400 {\n    return fmt.Errorf(\"http status code %d\", resp.StatusCode)\n}","preventionTips":["Use aria2 with custom Referer/User-Agent headers for protected links instead of the plain http tool","Pre-check URLs with HEAD requests before queuing bulk downloads","Prefer fresh, unexpired signed URLs"],"tags":["http","status-code","hotlink-protection","offline-download"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}