{"record":{"id":"044a91a7456548c3","repo":"GopeedLab/gopeed","slug":"cannot-change-ed2k-target-path-after-transfer-star","errorCode":null,"errorMessage":"cannot change ed2k target path after transfer started","messagePattern":"cannot change ed2k target path after transfer started","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/protocol/ed2k/fetcher.go","lineNumber":138,"sourceCode":"\t}\n\thandle, err = client.AddTransfer(atp)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.handle = handle\n\tif handle.IsValid() && handle.IsPaused() {\n\t\tif err := client.ResumeTransfer(handle.GetHash()); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (f *Fetcher) Patch(req *base.Request, opts *base.Options) error {\n\thandle := f.currentHandle()\n\n\tif opts != nil && (opts.Name != \"\" || opts.Path != \"\") && handle.IsValid() {\n\t\treturn errors.New(\"cannot change ed2k target path after transfer started\")\n\t}\n\tif req != nil && req.URL != \"\" && handle.IsValid() {\n\t\treturn errors.New(\"cannot change ed2k link after transfer started\")\n\t}\n\n\tif req != nil {\n\t\tif req.URL != \"\" {\n\t\t\tlink, err := parseLink(req.URL)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tf.meta.Req.URL = req.URL\n\t\t\tf.meta.Res = buildResource(link)\n\t\t}\n\t\tif req.Labels != nil {\n\t\t\tif f.meta.Req.Labels == nil {\n\t\t\t\tf.meta.Req.Labels = make(map[string]string)\n\t\t\t}","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/internal/protocol/ed2k/fetcher.go#L120-L156","documentation":"The ed2k Fetcher.Patch (internal/protocol/ed2k/fetcher.go:137-139) rejects changes to opts.Name or opts.Path once the transfer has a valid handle in the underlying goed2k client (currentHandle() finds a live or paused transfer). An eMule transfer is bound to the file path it was added with, so renaming or re-targeting it after start cannot be supported and is refused rather than silently ignored.","triggerScenarios":"Downloader.Patch(taskId, nil, &base.Options{Name: \"x\"}) or with Path set, on an ed2k task whose transfer already exists (previously started, even if now paused); patching path/name on a re-created/restored ed2k task where FindTransfer immediately returns a valid handle; UI flows that let users move a running download to another directory.","commonSituations":"A 'change save folder' or 'rename' button applied to a running or paused ed2k task; automation that patches Options (e.g. setting Path from a template) on every task regardless of protocol/state; restoring tasks from storage and then applying pending option changes.","solutions":["Patch name/path only before the task has started (no valid handle yet) — right after Resolve, before Start","For a running task: pause, remove/close the task, and re-create it with the new Name/Path from the same ed2k link","Restrict UI patch fields for ed2k to Labels and Proxy, which Patch does accept at any time","Check task status first and hide/disable rename/repath controls for started ed2k tasks"],"exampleFix":"// before\nerr := downloader.Patch(taskId, nil, &base.Options{Path: \"/data/new-dir\"}) // fails once started\n\n// after (restart the task with new path)\n_ = downloader.Pause(&download.TaskFilter{IDs: []string{taskId}})\n_ = downloader.Delete(&download.TaskFilter{IDs: []string{taskId}})\n_, err = downloader.CreateDirect(req, &base.Options{Path: \"/data/new-dir\"})","handlingStrategy":"validation","validationCode":"// Before patching an ed2k task with name/path, ensure it has not started:\ntask := downloader.GetTask(taskId)\nif task == nil { return download.ErrTaskNotFound }\nstarted := task.Status != base.DownloadStatusReady /* adjust to your states */\nif started && (opts.Name != \"\" || opts.Path != \"\") {\n    return errors.New(\"cannot change path of a started ed2k task\")\n}","typeGuard":null,"tryCatchPattern":"if err := downloader.Patch(taskId, nil, opts); err != nil {\n    if strings.Contains(err.Error(), \"cannot change ed2k target path\") {\n        // pause + delete + re-create with new options\n    }\n}","preventionTips":["Apply name/path options at task creation, not via Patch, for ed2k","Restrict edit UI for ed2k tasks to Labels/Proxy","Send patch fields selectively (zero-value means 'unchanged' here), so empty Name/Path are omitted"],"tags":["ed2k","patch","lifecycle","immutable-options"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}