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
- 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.
- Bypass or reconfigure proxies/CDNs that strip Content-Range, or use a direct storage URL.
- Retry with backoff — transient middlebox behavior can drop headers; retrying may recover.
- 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
- Check for Accept-Ranges: bytes and expect 206 responses before chunked downloads.
- Bypass/reconfigure proxies or CDNs that strip Content-Range.
- Use direct storage URLs (e.g. presigned) instead of intermediary gateways.
- Fall back to single-request downloads when range support is absent.
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
- unsupported content-range: %q
- unknown total size in content-range: %q
- parse range start: %w
- parse range end: %w
- parse total size: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/be903f30fed333dd.
Report an issue: GitHub.