{"record":{"id":"b3c3603dfa2b409f","repo":"AlistGo/alist","slug":"missing-file-path-and-id","errorCode":null,"errorMessage":"missing file path and id","messagePattern":"missing file path and id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/yunpan360/util.go","lineNumber":697,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &resp, nil\n}\n\nfunc (d *Yunpan360) openDownloadURL(ctx context.Context, file model.Obj) (*OpenDownloadResp, error) {\n\tvar resp OpenDownloadResp\n\tsignParams := map[string]string{}\n\tbody := map[string]string{}\n\n\tif file.GetPath() != \"\" {\n\t\tsignParams[\"fpath\"] = normalizeRemotePath(file.GetPath())\n\t\tbody[\"fpath\"] = signParams[\"fpath\"]\n\t} else if file.GetID() != \"\" {\n\t\tsignParams[\"nid\"] = file.GetID()\n\t\tbody[\"nid\"] = file.GetID()\n\t} else {\n\t\treturn nil, errors.New(\"missing file path and id\")\n\t}\n\n\terr := d.openPOST(ctx, \"MCP.getDownLoadUrl\", signParams, nil, body, &resp, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &resp, nil\n}\n\nfunc (d *Yunpan360) cookieMakeDir(ctx context.Context, fullPath string) (*CookieMkdirResp, error) {\n\tvar resp CookieMkdirResp\n\tbody := map[string]string{\n\t\t\"path\":      ensureDirAPIPath(fullPath),\n\t\t\"owner_qid\": \"0\",\n\t}\n\terr := d.cookieRequestForm(ctx, \"/file/mkdir\", body, &resp)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":679,"sourceCodeEnd":715,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/yunpan360/util.go#L679-L715","documentation":"openDownloadURL builds the MCP.getDownLoadUrl Open-API call from either fpath or nid. If the object has neither a path nor an ID, there is nothing to sign the request with and the guard fires (drivers/yunpan360/util.go:697).","triggerScenarios":"Calling the Open-API download path with a model.Obj whose GetPath() and GetID() are both empty. Happens with zero-value objects, root placeholders, or objects stripped by serialization before Link().","commonSituations":"Caller constructs model.Object{} without fields; object fields lost across JSON boundaries (omitempty dropping empty strings is fine here — the source never set them); passing the storage root (\"/\", empty id) to a file download call.","solutions":["Pass an object returned by List() — it always has path or nid populated","If building objects manually, set at least one of Path or ID (ID preferred)","Guard against download attempts on the root/directory objects in caller code"],"exampleFix":"// before\n_, err := d.openDownloadURL(ctx, &model.Object{})\n\n// after\n_, err := d.openDownloadURL(ctx, &model.Object{Path: \"/docs/a.txt\", ID: \"123456789\"})","handlingStrategy":"validation","validationCode":"if file.GetPath() == \"\" && file.GetID() == \"\" { return errors.New(\"object needs path or id\") }","typeGuard":"func identifiable(obj model.Obj) bool { return obj.GetPath() != \"\" || obj.GetID() != \"\" }","tryCatchPattern":"if !identifiable(file) { return nil, errors.New(\"re-list to obtain path/nid\") }\nreturn d.openDownloadURL(ctx, file)","preventionTips":["Enforce non-empty Path or ID at object-construction time in caller code","Block download attempts on root/mount objects","Use typed structs for objects crossing API boundaries"],"tags":["yunpan360","open-api","validation","download","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}