larksuite/cli · error

content-range is empty

Error message

content-range is empty

What it means

parseContentRange in extension/download parses the HTTP `Content-Range` header of a ranged (partial) download. When the header is missing or whitespace-only, it returns "content-range is empty". Callers openPartial/openNext rely on this header to compute chunk offsets, so a response without it cannot be assembled as a partial download.

Source

Thrown at extension/download/download.go:675

// contentRange is a parsed `Content-Range: bytes start-end/total` header.
type contentRange struct {
	start int64
	end   int64
	total int64
}

func (cr contentRange) String() string {
	return fmt.Sprintf("bytes %d-%d/%d", cr.start, cr.end, cr.total)
}

func (cr contentRange) length() int64 {
	return cr.end - cr.start + 1
}

func parseContentRange(header string) (contentRange, error) {
	header = strings.TrimSpace(header)
	if header == "" {
		return contentRange{}, fmt.Errorf("content-range is empty")
	}
	unit, spec, found := strings.Cut(header, " ")
	if !found || !strings.EqualFold(unit, "bytes") {
		return contentRange{}, fmt.Errorf("unsupported content-range: %q", header)
	}
	parts := strings.SplitN(strings.TrimSpace(spec), "/", 2)
	if len(parts) != 2 || parts[0] == "" || parts[1] == "" || parts[0] == "*" {
		return contentRange{}, fmt.Errorf("unsupported content-range: %q", header)
	}
	if parts[1] == "*" {
		return contentRange{}, fmt.Errorf("unknown total size in content-range: %q", header)
	}
	bounds := strings.SplitN(parts[0], "-", 2)
	if len(bounds) != 2 || bounds[0] == "" || bounds[1] == "" {
		return contentRange{}, fmt.Errorf("unsupported content-range: %q", header)
	}
	start, err := strconv.ParseInt(bounds[0], 10, 64)
	if err != nil {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Check the response: log status code and headers; a 200 (not 206) means the server ignored the Range request — fall back to a full single-shot download.
  2. Bypass or reconfigure proxies/CDNs that strip Content-Range, or use a direct storage URL.
  3. Retry with backoff — transient middlebox behavior can drop headers; retrying may recover.
  4. If the target regularly lacks range support, disable partial/resumable download and fetch the file whole.

Example fix

// before: assume every ranged response carries Content-Range
resp, _ := client.Do(rangeReq)
cr, err := parseContentRange(resp.Header.Get("Content-Range"))
// after: verify 206 + header presence and fall back to full download
if resp.StatusCode != http.StatusPartialContent || strings.TrimSpace(resp.Header.Get("Content-Range")) == "" {
	return downloadFully(url, dest) // plain GET, no chunking
}
cr, err := parseContentRange(resp.Header.Get("Content-Range"))
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight: confirm the server supports ranges before chunked download
req, _ := http.NewRequest("HEAD", url, nil)
resp, err := client.Do(req)
if err != nil { return err }
if resp.Header.Get("Accept-Ranges") != "bytes" {
	return errors.New("server does not advertise range support; use full download")
}

Type guard

func hasContentRange(h http.Header) bool {
	return strings.TrimSpace(h.Get("Content-Range")) != ""
}

Try / catch

cr, err := parseContentRange(resp.Header.Get("Content-Range"))
if err != nil {
	if strings.Contains(err.Error(), "content-range is empty") {
		// server ignored Range or stripped the header: retry once, then full download
		if !retryWithBackoff(ctx) {
			return downloadFully(url, dest)
		}
	}
	return err
}

Prevention

When it happens

Trigger: A ranged GET (Range: bytes=...) response that omits Content-Range: server returns 200 instead of 206 Partial Content, a proxy/CDN strips the header, or the endpoint does not support range requests; the header value is empty after trimming.

Common situations: Downloading from storage/CDN endpoints that ignore Range and return the full body; misconfigured caches or gateways stripping headers; resuming a download against a different server that no longer supports ranges.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/be903f30fed333dd. Report an issue: GitHub.