GopeedLab/gopeed · error
invalid blob url
Error message
invalid blob url
What it means
Returned by the blob Registry (internal/blob/registry.go) when a URL passed to Metadata/Acquire/Release/Revoke/SourceError/IsURL does not identify a source served by this registry. parseURL (registry.go:645-661) accepts only http URLs whose path starts with /__blob/, whose string form starts with the registry's current baseURL (host:port of its loopback server), and whose id is a single non-empty path segment. Anything else — a plain http(s) URL, a blob URL from a previous server generation, or a truncated/mangled URL — fails with this sentinel.
Source
Thrown at internal/blob/registry.go:30
"net/http"
"net/url"
"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)View on GitHub (pinned to 7b7327ffb3)
Solutions
- Use the exact string returned by Registry.CreateOpener/CreateBlob — never construct or edit a blob URL manually
- Do not persist blob URLs; they are valid only for the lifetime of the Registry instance that created them (sources die with Registry.Close and the port changes on restart)
- If unsure, gate the call with registry.IsURL(raw) (registry.go:106) which returns false instead of erroring
- If you stored the URL, re-create the source (CreateOpener) and re-resolve to get a fresh URL
Example fix
// before
url := "http://127.0.0.1:39821/__blob/abc" // copied from a previous run
err := registry.Acquire(url)
// after
url, err := registry.CreateOpener(open, opts)
if err != nil { return err }
err = registry.Acquire(url) // exact string from CreateOpener Defensive patterns
Strategy: validation
Validate before calling
// Only operate on URLs this registry issued, in this process:
if !registry.IsURL(raw) {
// not a blob URL: handle as a normal http URL or drop it
}
err := registry.Acquire(raw) Type guard
func isBlobURL(r *blob.Registry, raw string) bool { return r.IsURL(raw) } Try / catch
if err := registry.Acquire(raw); err != nil {
if errors.Is(err, blob.ErrInvalidURL) {
// URL is not from this registry (stale or foreign): re-create source or ignore
}
} Prevention
- Never persist or hand-edit blob URLs; regenerate them each run via CreateOpener/CreateBlob
- Pass the exact string returned by CreateOpener through every layer (no re-parsing/re-encoding)
- Gate every blob call with IsURL when the URL might come from user input or storage
When it happens
Trigger: Calling registry.Acquire/Metadata/Release/Revoke with a normal download URL instead of a URL returned by CreateOpener/CreateBlob; persisting a blob URL across a Registry restart (baseURL is regenerated with a new random port each time ensureServerLocked runs, so the stored prefix no longer matches); URL-encoding or path manipulation that makes the id a nested path (path.Base(id) != id).
Common situations: Treating blob:///__blob/<id> or the raw id as the URL; saving blob URLs to disk and reusing them next run; concatenating the base URL yourself instead of using the returned src.URL; passing the URL with a trailing slash or query string appended by other code.
Related errors
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/b26e400b0f4a9f3c.
Report an issue: GitHub.