{"record":{"id":"01bc8ec3f7ee000c","repo":"AlistGo/alist","slug":"get-size-from-url-s-failed-status-code-d","errorCode":null,"errorMessage":"get size from url %s failed, status code: %d","messagePattern":"get size from url (.+?) failed, status code: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"drivers/url_tree/util.go","lineNumber":188,"sourceCode":"\t\treturn nil, errs.ObjectNotFound\n\t}\n\treturn &model.Object{\n\t\tName:     node.Name,\n\t\tSize:     node.Size,\n\t\tModified: time.Unix(node.Modified, 0),\n\t\tIsFolder: !node.isFile(),\n\t\tPath:     path,\n\t}, nil\n}\n\nfunc getSizeFromUrl(url string) (int64, error) {\n\tres, err := base.RestyClient.R().SetDoNotParseResponse(true).Head(url)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer res.RawResponse.Body.Close()\n\tif res.StatusCode() >= 300 {\n\t\treturn 0, fmt.Errorf(\"get size from url %s failed, status code: %d\", url, res.StatusCode())\n\t}\n\tsize, err := strconv.ParseInt(res.Header().Get(\"Content-Length\"), 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn size, nil\n}\n\nfunc StringifyTree(node *Node) string {\n\tsb := strings.Builder{}\n\tif node.Level == -1 {\n\t\tfor i, child := range node.Children {\n\t\t\tsb.WriteString(StringifyTree(child))\n\t\t\tif i < len(node.Children)-1 {\n\t\t\t\tsb.WriteString(\"\\n\")\n\t\t\t}\n\t\t}\n\t\treturn sb.String()","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/url_tree/util.go#L170-L206","documentation":"With head_size enabled, the url_tree driver issues a HEAD request to each file URL to learn Content-Length. If the server answers with a status >= 300 (redirect chains not followed to a 2xx, 403 auth wall, 404, 429), getSizeFromUrl reports the URL and status code. The node is still created (size stays 0) because the caller only logs this error.","triggerScenarios":"HEAD request to a signed/expired URL (403), a URL behind Cloudflare or bot protection (403/429), a redirect loop, or a typo'd/dead origin (404/5xx) during BuildTree with head_size on.","commonSituations":"Time-limited signed URLs in the config, origins that reject HEAD (some return 405), or anti-bot layers. Noticeable as files showing 0 size in the mount.","solutions":["Verify the URL with curl -I; fix expiry/signature or use a stable URL.","Disable head_size and provide explicit sizes in the structure if the origin cannot serve HEAD reliably.","If the origin rejects HEAD with 405/403 but allows GET, proxy it or precompute sizes; some servers can be configured to allow HEAD."],"exampleFix":"// before: url with expired signature\nfile https://cdn/x.mp4?sig=expired\n// after: refresh the URL or disable head_size and set size inline\nfile.mp4:1234567: https://cdn/x.mp4","handlingStrategy":"fallback","validationCode":"func checkHEADOk(fileURL string) bool {\n    res, err := base.RestyClient.R().Head(fileURL)\n    return err == nil && res.StatusCode() < 300\n}","typeGuard":null,"tryCatchPattern":"// in-tree behavior: error is logged, size stays 0 — callers should tolerate size 0\nif size, err := getSizeFromUrl(u); err != nil {\n    log.Warnf(\"size probe failed (%v); using 0\", err)\n    size = 0\n}","preventionTips":["Verify URLs with curl -I before enabling head_size","Prefer explicit sizes in the structure for signed/expiring URLs","Disable head_size for origins that block HEAD or bot-protect paths"],"tags":["url-tree","http","head-request","size-probe"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}