{"record":{"id":"67a6f7f0ce55aa81","repo":"AlistGo/alist","slug":"missing-file-path","errorCode":null,"errorMessage":"missing file path","messagePattern":"missing file path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/yunpan360/util.go","lineNumber":349,"sourceCode":"func (d *Yunpan360) cookieDownloadURL(ctx context.Context, file model.Obj) (*CookieDownloadResp, error) {\n\tresp, err := d.cookieDownloadURLOnce(ctx, file, false)\n\tif err == nil {\n\t\treturn resp, nil\n\t}\n\n\td.invalidateCookieDownloadSession()\n\treturn d.cookieDownloadURLOnce(ctx, file, true)\n}\n\nfunc (d *Yunpan360) cookieDownloadURLOnce(ctx context.Context, file model.Obj, refresh bool) (*CookieDownloadResp, error) {\n\tnid := strings.TrimSpace(file.GetID())\n\tif nid == \"\" {\n\t\treturn nil, errors.New(\"missing file id\")\n\t}\n\n\tfname := normalizeRemotePath(file.GetPath())\n\tif fname == \"\" {\n\t\treturn nil, errors.New(\"missing file path\")\n\t}\n\n\townerQID, token, err := d.resolveCookieDownloadParams(ctx, file, refresh)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar resp CookieDownloadResp\n\terr = d.cookieRequestForm(ctx, downloadPath, map[string]string{\n\t\t\"nid\":       nid,\n\t\t\"fname\":     fname,\n\t\t\"owner_qid\": ownerQID,\n\t\t\"token\":     token,\n\t}, &resp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &resp, nil","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/yunpan360/util.go#L331-L367","documentation":"A guard in cookieDownloadURLOnce: the cookie-mode download form requires fname (the normalized remote path). If normalizeRemotePath(file.GetPath()) yields an empty string, the request is rejected before it is sent (drivers/yunpan360/util.go:349).","triggerScenarios":"Calling download with a model.Obj whose Path is empty, \"/\", or only whitespace/slashes so that normalizeRemotePath returns \"\". Typical with hand-constructed objects or objects whose path was dropped during serialization.","commonSituations":"Caller builds model.Obj from just an ID and forgets Path; object passed through JSON round-trip where path field was tagged differently (path vs name confusion); root-level objects with empty paths used for download.","solutions":["Populate the object's Path from the listing before calling download (e.g. join parent path + name)","Verify the obj passed to Link comes from the driver's List output","If only the ID is known, first List the parent to obtain the full path"],"exampleFix":"// before\nobj := &model.Object{ID: \"123456789\"} // path empty\n\n// after\nobj := &model.Object{ID: \"123456789\", Path: \"/docs/a.txt\", Name: \"a.txt\"}","handlingStrategy":"validation","validationCode":"if normalizeRemotePath(file.GetPath()) == \"\" { return errors.New(\"refusing download: object has no path\") }","typeGuard":"func hasFilePath(obj model.Obj) bool { return normalizeRemotePath(obj.GetPath()) != \"\" }","tryCatchPattern":"if !hasFilePath(file) { return nil, errors.New(\"object lacks path; re-list to populate\") }\nreturn d.cookieDownloadURL(ctx, file)","preventionTips":["When building objects, always compute Path as parent + '/' + name","Round-trip objects through typed structs, not ad-hoc maps, to keep Path","Validate both ID and Path before calling Link()"],"tags":["yunpan360","validation","download","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}