GopeedLab/gopeed · error
ed2k link is empty
Error message
ed2k link is empty
What it means
Fetcher.hash (internal/protocol/ed2k/fetcher.go:310-313) returns this when f.meta or f.meta.Req is nil — the fetcher has no request at all, so there is no ed2k link to derive a transfer hash from. Methods that need the hash (currentHandle, Pause, Stats, Close paths) fail with it when the fetcher was never given a request.
Source
Thrown at internal/protocol/ed2k/fetcher.go:312
}
client := f.manager.currentClient()
if client == nil {
return f.handle
}
hash, err := f.hash()
if err != nil {
return f.handle
}
handle := client.FindTransfer(hash)
if handle.IsValid() {
f.handle = handle
}
return f.handle
}
func (f *Fetcher) hash() (gprotocol.Hash, error) {
if f.meta == nil || f.meta.Req == nil {
return gprotocol.Invalid, errors.New("ed2k link is empty")
}
link, err := parseLink(f.meta.Req.URL)
if err != nil {
return gprotocol.Invalid, err
}
return link.Hash, nil
}
type FetcherManager struct {
mu sync.Mutex
client *goed2k.Client
stateStore *clientStateStore
}
func (fm *FetcherManager) SetStateStore(store fetcher.ProtocolStateStore) {
fm.mu.Lock()
defer fm.mu.Unlock()
View on GitHub (pinned to 7b7327ffb3)
Solutions
- Always create ed2k fetchers through FetcherManager (it wires meta and client state)
- Ensure Resolve/setup completed successfully before calling Start/Pause/Stats
- If you construct the fetcher yourself, set f.meta = &fetcher.FetcherMeta{Req: req} before use
- Guard callers: if fetcher.Meta() == nil || fetcher.Meta().Req == nil, skip hash-dependent operations
Example fix
// before
f := &ed2k.Fetcher{}
_ = f.Pause() // "ed2k link is empty"
// after
f := manager.BuildFetcher(url) // via FetcherManager
// or manually:
f.Meta().Req = &base.Request{URL: "ed2k://|file|...|/"} Defensive patterns
Strategy: validation
Validate before calling
// Ensure the fetcher has a request before hash-dependent calls:
meta := fetcher.Meta()
if meta == nil || meta.Req == nil || meta.Req.URL == "" {
return errors.New("ed2k fetcher not initialized")
}
err := fetcher.Pause() Type guard
func ed2kReady(f *ed2k.Fetcher) bool {
m := f.Meta()
return m != nil && m.Req != nil && m.Req.URL != ""
} Try / catch
if err := fetcher.Pause(); err != nil {
if strings.Contains(err.Error(), "ed2k link is empty") {
// initialization order bug: run Resolve/Setup first
}
} Prevention
- Always build ed2k fetchers via FetcherManager, never struct literals
- Complete Resolve before Start/Pause/Stats
- In tests, set fetcher.Meta().Req before exercising hash paths
When it happens
Trigger: Using a zero-value Fetcher{} instead of constructing via FetcherManager; calling Pause/Stats before Resolve has stored meta.Req; code that builds the fetcher manually (protocol registry bypass) and skips setting fetcher.Meta().Req; calling hash-dependent methods after a failed/aborted initialization left meta nil.
Common situations: Unit tests instantiating ed2k.Fetcher directly; a manager bug that hands out a fetcher before Setup/Resolve completes; API-level calls arriving for a task whose fetcher was restored without its request metadata.
Related errors
- cannot change ed2k target path after transfer started
- cannot change ed2k link after transfer started
- unsupported ed2k link type
- webview is not initialized
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/6bea81bb675b6fc4.
Report an issue: GitHub.