GopeedLab/gopeed · error

invalid blob options

Error message

invalid blob options

What it means

Returned by Registry.CreateOpener when the caller-supplied opener is invalid: either open == nil (registry.go:136-138), or the options ask for range support without a usable size — opts.Range == true with opts.Size <= 0 fails with the wrapped message 'invalid blob options: range requires positive size' (registry.go:145-147). The registry needs a real OpenFunc to serve bytes and a positive size to advertise and validate Range requests.

Source

Thrown at internal/blob/registry.go:31

	"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. Always pass a non-nil OpenFunc that returns an io.ReadCloser
  2. Set CreateOptions.Size to the exact byte length when Range: true (size >= 1)
  3. If the length is unknown, set Range: false — the source will then be served as a whole-body stream and parseRange is skipped
  4. Check the wrapped message with errors.Is(err, ErrInvalidOptions) to distinguish the size problem from the nil-opener problem

Example fix

// before
url, err := registry.CreateOpener(openFn, &blob.CreateOptions{ContentType: "video/mp4", Range: true}) // Size missing

// after
url, err := registry.CreateOpener(openFn, &blob.CreateOptions{ContentType: "video/mp4", Range: true, Size: totalBytes})
Defensive patterns

Strategy: validation

Validate before calling

if open == nil { return errors.New("open func required") }
if opts.Range && opts.Size <= 0 { opts.Range = false /* or fail here */ }
url, err := registry.CreateOpener(open, opts)

Try / catch

url, err := registry.CreateOpener(open, opts)
if err != nil {
    if errors.Is(err, blob.ErrInvalidOptions) {
        // nil opener or Range without positive Size: fix CreateOptions and retry once
    }
}

Prevention

When it happens

Trigger: Calling CreateOpener(nil, opts); passing &CreateOptions{Range: true} with Size unset (zero) or negative; computing size lazily and forgetting to set it when enabling Range; passing opts with Size set but Range enabled for a stream whose length is genuinely unknown.

Common situations: Copying a CreateBlob-style call but hand-rolling CreateOpener and omitting Size; enabling Range by default for all sources regardless of whether the length is known; refactoring that accidentally drops the open callback parameter.

Related errors


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