AlistGo/alist · warning · ErrNoOverlap

invalid range: failed to overlap

Error message

invalid range: failed to overlap

What it means

ErrNoOverlap from pkg/http_range.ParseRange: every byte-range-spec in a Range header starts beyond the content size, so none of the requested ranges intersect the resource. Returned when parsing a header like `bytes=5000-` against a 100-byte file.

Source

Thrown at pkg/http_range/range.go:27

	"strconv"
	"strings"
)

// Range specifies the byte range to be sent to the client.
type Range struct {
	Start  int64
	Length int64 // limit of bytes to read, -1 for unlimited
}

// ContentRange returns Content-Range header value.
func (r Range) ContentRange(size int64) string {
	return fmt.Sprintf("bytes %d-%d/%d", r.Start, r.Start+r.Length-1, size)
}

var (
	// ErrNoOverlap is returned by ParseRange if first-byte-pos of
	// all the byte-range-spec values is greater than the content size.
	ErrNoOverlap = errors.New("invalid range: failed to overlap")

	// ErrInvalid is returned by ParseRange on invalid input.
	ErrInvalid = errors.New("invalid range")
)

// ParseRange parses a Range header string as per RFC 7233.
// ErrNoOverlap is returned if none of the ranges overlap.
// ErrInvalid is returned if s is invalid range.
func ParseRange(s string, size int64) ([]Range, error) { // nolint:gocognit
	if s == "" {
		return nil, nil // header not present
	}
	const b = "bytes="
	if !strings.HasPrefix(s, b) {
		return nil, ErrInvalid
	}
	var ranges []Range
	noOverlap := false

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Verify the size argument passed to ParseRange is the real current content length (re-stat the remote resource)
  2. If the client's range is legitimately past EOF (e.g. resume of a completed download), treat 416 semantics: satisfy the request with the full resource or return 416 with a Content-Range of the actual size per RFC 7233
  3. Clamp/validate requested offsets against size before calling ParseRange

Example fix

// before
ranges, err := http_range.ParseRange(r.Header.Get("Range"), staleSize)

// after
ranges, err := http_range.ParseRange(r.Header.Get("Range"), currentSize)
if errors.Is(err, http_range.ErrNoOverlap) {
    w.Header().Set("Content-Range", fmt.Sprintf("bytes */%d", currentSize))
    http.Error(w, "Requested Range Not Satisfiable", http.StatusRequestedRangeNotSatisfiable)
    return
}
Defensive patterns

Strategy: try-catch

Validate before calling

if start := requestedStart(); start >= 0 && start >= currentSize {
	// client already has everything; answer 416 or full body per RFC 7233
}

Try / catch

ranges, err := http_range.ParseRange(hdr, size)
if errors.Is(err, http_range.ErrNoOverlap) {
	w.Header().Set("Content-Range", fmt.Sprintf("bytes */%d", size))
	http.Error(w, "range not satisfiable", http.StatusRequestedRangeNotSatisfiable)
	return
}

Prevention

When it happens

Trigger: Calling http_range.ParseRange("bytes=5000-6000", 100) where start offset >= size for all specs; typically a client resuming a partial download against a truncated or replaced file.

Common situations: A downloader cached a file length from a previous version and the server file shrank, or the code passes the wrong size (0 or stale) to ParseRange.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/9ea67d2aa7939c49. Report an issue: GitHub.