{"record":{"id":"dd1103a1fb16dc9b","repo":"AlistGo/alist","slug":"empty-download-url","errorCode":null,"errorMessage":"empty download url","messagePattern":"empty download url","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/gitee/driver.go","lineNumber":90,"sourceCode":"\tobjs := make([]model.Obj, 0, len(contents))\n\tfor i := range contents {\n\t\tobjs = append(objs, contents[i].toModelObj())\n\t}\n\treturn objs, nil\n}\n\nfunc (d *Gitee) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {\n\tvar downloadURL string\n\tif obj, ok := file.(*Object); ok {\n\t\tdownloadURL = obj.DownloadURL\n\t\tif downloadURL == \"\" {\n\t\t\trelPath := d.relativePath(file.GetPath())\n\t\t\tcontent, err := d.getContent(relPath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif content.DownloadURL == \"\" {\n\t\t\t\treturn nil, errors.New(\"empty download url\")\n\t\t\t}\n\t\t\tobj.DownloadURL = content.DownloadURL\n\t\t\tdownloadURL = content.DownloadURL\n\t\t}\n\t} else {\n\t\trelPath := d.relativePath(file.GetPath())\n\t\tcontent, err := d.getContent(relPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif content.DownloadURL == \"\" {\n\t\t\treturn nil, errors.New(\"empty download url\")\n\t\t}\n\t\tdownloadURL = content.DownloadURL\n\t}\n\turl := d.applyProxy(downloadURL)\n\treturn &model.Link{\n\t\tURL: url,","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/gitee/driver.go#L72-L108","documentation":"During Link() the driver had a cached *Object whose DownloadURL was empty, so it re-fetched the file content via the Gitee contents API; the fresh response also had an empty DownloadURL, meaning Gitee did not supply a raw-file link for this path. The driver refuses to return a link that cannot be downloaded.","triggerScenarios":"Calling Link() on a file whose *Object.DownloadURL is empty AND whose GET /repos/{owner}/{repo}/contents/{path} response has an empty download_url — typical for empty files, LFS-pointer files, files in private repos with no token, or unsupported content encodings. drivers/gitee/driver.go:90.","commonSituations":"Zero-byte file where Gitee omits download_url; private repository accessed without an access token so the contents API returns limited fields; file stored with Git LFS where the API returns a pointer instead of a blob URL; API change in the contents response shape.","solutions":["Verify the access token is configured and has read access to the private repo (contents API then returns download_url).","Check whether the file is empty or an LFS pointer; if so, download via git or configure LFS handling rather than the raw URL.","Re-list the directory so the cached Object is refreshed from a current API response.","If the API response shape changed, update the Content struct mapping in the driver."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if obj, ok := file.(*Object); ok && obj.DownloadURL != \"\" {\n    // fast path: no API call needed\n    return &model.Link{URL: d.applyProxy(obj.DownloadURL)}, nil\n}","typeGuard":"func hasDownloadURL(o model.Obj) bool {\n    if obj, ok := o.(*Object); ok {\n        return obj.DownloadURL != \"\"\n    }\n    return false\n}","tryCatchPattern":"link, err := d.Link(ctx, file, args)\nif err != nil && err.Error() == \"empty download url\" {\n    // refresh listing and retry once\n    op.RefreshDir(ctx, parentPath)\n    link, err = d.Link(ctx, file, args)\n}","preventionTips":["Keep directory listings fresh so cached objects carry download URLs.","Always configure a token for private repos.","Treat empty/LFS files specially before requesting links."],"tags":["gitee","download","link","private-repo","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}