GopeedLab/gopeed · warning
blob range not allowed
Error message
blob range not allowed
What it means
HTTP 416 whose body is ErrRangeNotAllowed ('blob range not allowed'). parseRange returns it when the request carries a Range header and the source has range support enabled, but its registered size is not positive (size <= 0). The public CreateOpener API already rejects Range:true together with Size<=0 ('range requires positive size'), so through normal API use this is a defensive branch — it fires only for range-enabled sources whose recorded size is zero.
Source
Thrown at internal/blob/registry.go:338
if err != nil {
http.NotFound(w, req)
return
}
meta, open, session := src.acquireOpen()
if open == nil {
http.NotFound(w, req)
return
}
if session != nil {
defer session.Release()
}
if src.sourceError() != nil {
http.Error(w, "blob source unavailable", http.StatusGone)
return
}
start, end, ranged, err := parseRange(req.Header.Get("Range"), meta.Size, meta.Range)
if err != nil {
http.Error(w, err.Error(), http.StatusRequestedRangeNotSatisfiable)
return
}
if !meta.Range {
start, end, ranged = 0, -1, false
}
reader, err := open(req.Context(), OpenRequest{
Offset: start,
End: end,
})
if err != nil {
if req.Context().Err() == nil || !errors.Is(err, req.Context().Err()) {
src.recordSourceError(err, meta.Range)
}
http.Error(w, "blob source unavailable", http.StatusGone)
return
}
if reader == nil {
err = fmt.Errorf("%w: opener returned a nil reader", ErrSourceClosed)View on GitHub (pinned to 7b7327ffb3)
Solutions
- Check Registry.Metadata(url) first: only send Range when meta.Range is true AND meta.Size > 0; otherwise omit the header
- When creating sources, pair Range:true with a positive Size, or use Range:false for empty/unknown-size blobs (CreateOpener enforces this)
- Drop the Range header and re-request — non-ranged requests bypass range handling entirely
Example fix
// before
url, _ := reg.CreateOpener(open, &blob.CreateOptions{Range: true}) // Size 0: invalid combo
req.Header.Set("Range", "bytes=0-") // -> 416 blob range not allowed
// after
url, _ := reg.CreateOpener(open, &blob.CreateOptions{Range: false}) // empty blob: no ranges
// or register the real size:
// &blob.CreateOptions{Range: true, Size: size} Defensive patterns
Strategy: validation
Validate before calling
meta, err := reg.Metadata(blobURL)
if err != nil {
return err
}
if !meta.Range || meta.Size <= 0 {
req.Header.Del("Range") // never send Range to non-ranged or empty sources
} Try / catch
resp, err := client.Get(blobURL)
if err == nil && resp.StatusCode == http.StatusRequestedRangeNotSatisfiable { // 416
if strings.Contains(resp.Status, "range not allowed") || true {
// retry once without the Range header
req.Header.Del("Range")
}
} Prevention
- Only attach Range headers when Metadata reports Range:true and Size > 0
- Register ranges with a positive size or leave Range:false for empty blobs
- Strip default Range headers in shared HTTP clients before blob requests
When it happens
Trigger: A GET with any Range header (e.g. 'Range: bytes=0-') against a source registered with Range:true and a zero/negative size — reachable via hand-constructed Sources, test fixtures, or code paths that bypass CreateOpener's option validation.
Common situations: Test code building Sources directly; empty-blob handling where a client attaches a default Range header to every request; future refactors that change how size is recorded.
Related errors
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/22ea8a30a461ee26.
Report an issue: GitHub.