GopeedLab/gopeed · error
cannot change ed2k target path after transfer started
Error message
cannot change ed2k target path after transfer started
What it means
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.
Source
Thrown at internal/protocol/ed2k/fetcher.go:138
}
handle, err = client.AddTransfer(atp)
if err != nil {
return err
}
f.handle = handle
if handle.IsValid() && handle.IsPaused() {
if err := client.ResumeTransfer(handle.GetHash()); err != nil {
return err
}
}
return nil
}
func (f *Fetcher) Patch(req *base.Request, opts *base.Options) error {
handle := f.currentHandle()
if opts != nil && (opts.Name != "" || opts.Path != "") && handle.IsValid() {
return errors.New("cannot change ed2k target path after transfer started")
}
if req != nil && req.URL != "" && handle.IsValid() {
return errors.New("cannot change ed2k link after transfer started")
}
if req != nil {
if req.URL != "" {
link, err := parseLink(req.URL)
if err != nil {
return err
}
f.meta.Req.URL = req.URL
f.meta.Res = buildResource(link)
}
if req.Labels != nil {
if f.meta.Req.Labels == nil {
f.meta.Req.Labels = make(map[string]string)
}View on GitHub (pinned to 7b7327ffb3)
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
Example fix
// before
err := downloader.Patch(taskId, nil, &base.Options{Path: "/data/new-dir"}) // fails once started
// after (restart the task with new path)
_ = downloader.Pause(&download.TaskFilter{IDs: []string{taskId}})
_ = downloader.Delete(&download.TaskFilter{IDs: []string{taskId}})
_, err = downloader.CreateDirect(req, &base.Options{Path: "/data/new-dir"}) Defensive patterns
Strategy: validation
Validate before calling
// Before patching an ed2k task with name/path, ensure it has not started:
task := downloader.GetTask(taskId)
if task == nil { return download.ErrTaskNotFound }
started := task.Status != base.DownloadStatusReady /* adjust to your states */
if started && (opts.Name != "" || opts.Path != "") {
return errors.New("cannot change path of a started ed2k task")
} Try / catch
if err := downloader.Patch(taskId, nil, opts); err != nil {
if strings.Contains(err.Error(), "cannot change ed2k target path") {
// pause + delete + re-create with new options
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cannot change ed2k link after transfer started
- blob source not found
- blob source revoked
- blob source closed
- ed2k link is empty
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/044a91a7456548c3.
Report an issue: GitHub.