{"record":{"id":"37c111fd12a06b18","repo":"AlistGo/alist","slug":"server-returns-no-url","errorCode":null,"errorMessage":"server returns no url","messagePattern":"server returns no url","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/cloudreve_v4/driver.go","lineNumber":146,"sourceCode":"\t\t\t},\n\t\t\tThumbnail: thumb,\n\t\t}, nil\n\t})\n}\n\nfunc (d *CloudreveV4) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {\n\tvar url FileUrlResp\n\terr := d.request(http.MethodPost, \"/file/url\", func(req *resty.Request) {\n\t\treq.SetBody(base.Json{\n\t\t\t\"uris\":     []string{file.GetPath()},\n\t\t\t\"download\": true,\n\t\t})\n\t}, &url)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(url.Urls) == 0 {\n\t\treturn nil, errors.New(\"server returns no url\")\n\t}\n\texp := time.Until(url.Expires)\n\treturn &model.Link{\n\t\tURL:        url.Urls[0].URL,\n\t\tExpiration: &exp,\n\t}, nil\n}\n\nfunc (d *CloudreveV4) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {\n\treturn d.request(http.MethodPost, \"/file/create\", func(req *resty.Request) {\n\t\treq.SetBody(base.Json{\n\t\t\t\"type\":              \"folder\",\n\t\t\t\"uri\":               parentDir.GetPath() + \"/\" + dirName,\n\t\t\t\"error_on_conflict\": true,\n\t\t})\n\t}, nil)\n}\n","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/cloudreve_v4/driver.go#L128-L164","documentation":"CloudreveV4 Link() calls POST /file/url with the file path and download=true; the call succeeds but the response contains zero URLs. The server agreed to the request yet produced no downloadable link, so the driver cannot build a model.Link.","triggerScenarios":"Any download/link request (streaming a file, copying URL, preview) where url.Urls is empty. Occurs when the file's storage policy provides no direct link, the file is archived/recycled, or the V4 server returns an empty list for unsupported URI forms.","commonSituations":"File stored on a policy without direct-link support (e.g. server-proxied only); path passed with a leading/trailing form the server does not resolve; file deleted between listing and download; Cloudrebve V4 build where /file/url requires different body fields.","solutions":["Retry the download after refreshing the listing to confirm the file still exists.","Check that the file's storage policy in Cloudrebve V4 allows direct download links.","Test POST /file/url manually with the same body and inspect the full JSON to see why the list is empty.","Update to the latest driver version if the V4 API shape changed (field names like uris/download)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// guard before using the response\nif len(url.Urls) == 0 {\n    return nil, errors.New(\"server returns no url\")\n}\n// caller-side: verify file still listed before requesting a link\nif _, err := d.getFileInfo(ctx, file.GetPath()); err != nil {\n    return nil, fmt.Errorf(\"file missing before link fetch: %w\", err)\n}","typeGuard":"func hasUsableUrl(r FileUrlResp) bool {\n    return len(r.Urls) > 0 && r.Urls[0].URL != \"\" && time.Until(r.Expires) > 0\n}","tryCatchPattern":"link, err := d.Link(ctx, file, args)\nif err != nil {\n    if err.Error() == \"server returns no url\" {\n        // re-list the parent to refresh state, then retry once\n        d.List(ctx, parent)\n        link, err = d.Link(ctx, file, args)\n    }\n    if err != nil { return nil, err }\n}","preventionTips":["Ensure the file's storage policy supports direct download links","Refresh listings before download attempts on long-lived mounts","Report empty-url responses to the V4 server logs for diagnosis"],"tags":["cloudreve-v4","download","link","api-error","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}