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

  1. Always create ed2k fetchers through FetcherManager (it wires meta and client state)
  2. Ensure Resolve/setup completed successfully before calling Start/Pause/Stats
  3. If you construct the fetcher yourself, set f.meta = &fetcher.FetcherMeta{Req: req} before use
  4. 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

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


AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16). Data as JSON: /api/errors/6bea81bb675b6fc4. Report an issue: GitHub.