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
- Always pass a non-nil OpenFunc that returns an io.ReadCloser
- Set CreateOptions.Size to the exact byte length when Range: true (size >= 1)
- If the length is unknown, set Range: false — the source will then be served as a whole-body stream and parseRange is skipped
- 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
- Always pair Range:true with the exact known byte Size
- Write a small wrapper (createRangeSource(open, size, contentType)) so options are always consistent
- Unit-test source creation with edge sizes (0, 1, huge)
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.