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

  1. Use the exact string returned by Registry.CreateOpener/CreateBlob — never construct or edit a blob URL manually
  2. 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)
  3. If unsure, gate the call with registry.IsURL(raw) (registry.go:106) which returns false instead of erroring
  4. 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

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.