GopeedLab/gopeed · error
blob source not found
Error message
blob source not found
What it means
Sentinel returned by Registry.getByID (registry.go:638-642) when the id parsed out of a blob URL has no entry in the sources map. The URL is well-formed for this registry, but the source it names no longer (or never did) exist. Note CreateOpener on a nil Registry also returns this error (registry.go:133-135).
Source
Thrown at internal/blob/registry.go:32
"path"
"strconv"
"strings"
"sync"
"time"
)
const urlPathPrefix = "/__blob/"
const rangeSourceFailureLimit = 2
// unclaimedSourceTTL bounds how long a session-backed source may keep its
// engine alive without ever being claimed by a download task.
var unclaimedSourceTTL = 10 * time.Minute
var (
ErrInvalidURL = errors.New("invalid blob url")
ErrInvalidOptions = errors.New("invalid blob options")
ErrSourceNotFound = errors.New("blob source not found")
ErrSourceRevoked = errors.New("blob source revoked")
ErrSourceClosed = errors.New("blob source closed")
ErrRangeNotAllowed = errors.New("blob range not allowed")
)
type SessionRef interface {
Retain()
Release()
}
type OpenRequest struct {
Offset int64
End int64
}
type OpenFunc func(ctx context.Context, req OpenRequest) (io.ReadCloser, error)
type CreateOptions struct {View on GitHub (pinned to 7b7327ffb3)
Solutions
- Treat this error as terminal for the source: re-create the source (CreateOpener/CreateBlob) and re-create/patch the task with the new URL
- Call Downloader-level create promptly after creating the source so it gets Acquired before the unclaimed TTL fires
- Pair every Acquire with exactly one Release and remember the first final Release deletes the source
- On shutdown, stop issuing blob operations before calling Registry.Close
Example fix
// before
if err := registry.Acquire(blobURL); err != nil { log.Fatal(err) } // crashes after restart
// after
if err := registry.Acquire(blobURL); err != nil {
if errors.Is(err, blob.ErrSourceNotFound) {
blobURL, err = registry.CreateOpener(open, opts) // recreate and rebind
if err != nil { return err }
} else {
return err
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if !registry.IsURL(blobURL) { /* will be ErrInvalidURL, not NotFound */ }
// Nothing can prove existence cheaper than the call itself; just handle the error. Try / catch
if err := registry.Acquire(blobURL); err != nil {
if errors.Is(err, blob.ErrSourceNotFound) {
// terminal: recreate source and rebind task URL
blobURL, err = registry.CreateOpener(open, opts)
if err != nil { return err }
err = registry.Acquire(blobURL)
}
if err != nil { return err }
} Prevention
- Acquire immediately after creating the source (before the 10-minute unclaimed TTL can fire)
- Remember the final Release deletes the source — never use the URL afterwards
- Don't expect blob sources to survive Registry.Close or process restart
When it happens
Trigger: Calling Acquire/Release/Metadata/Revoke after the source was already deleted: the final Release deletes the source (registry.go:229), Revoke removes it, Registry.Close clears all sources, or a session-backed source expired via the 10-minute unclaimedSourceTTL (registry.go:27,177-185). Also using an rrId/blob URL against a fresh Registry after an app restart, or calling CreateOpener on a nil (*Registry)(nil) receiver.
Common situations: Re-resolving or resuming a task after process restart with a blob URL captured from the previous run; double-Release (first Release succeeds and deletes, second gets NotFound); delaying task creation more than 10 minutes after a session-backed source was created; racing with Registry.Close during shutdown.
Related errors
- blob source revoked
- invalid blob url
- blob source closed
- invalid blob options
- cannot change ed2k target path after transfer started
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/f6743e1c7a7f0be4.
Report an issue: GitHub.