larksuite/cli · error

invalid content range: start %d is after end %d

Error message

invalid content range: start %d is after end %d

What it means

parseContentRange validated all numeric fields of a Content-Range header and found start > end - an unsatisfiable or bogus range from the server. Earlier checks already rejected negatives and non-integers, so both values are well-formed non-negative integers in the wrong order.

Source

Thrown at extension/download/download.go:711

	if err != nil {
		return contentRange{}, fmt.Errorf("parse range start: %w", err)
	}
	end, err := strconv.ParseInt(bounds[1], 10, 64)
	if err != nil {
		return contentRange{}, fmt.Errorf("parse range end: %w", err)
	}
	total, err := strconv.ParseInt(parts[1], 10, 64)
	if err != nil {
		return contentRange{}, fmt.Errorf("parse total size: %w", err)
	}
	if total <= 0 {
		return contentRange{}, fmt.Errorf("invalid total size: %d", total)
	}
	if start < 0 || end < 0 {
		return contentRange{}, fmt.Errorf("invalid negative content range: %d-%d", start, end)
	}
	if start > end {
		return contentRange{}, fmt.Errorf("invalid content range: start %d is after end %d", start, end)
	}
	if end >= total {
		return contentRange{}, fmt.Errorf("invalid content range: end %d is outside total %d", end, total)
	}
	return contentRange{start: start, end: end, total: total}, nil
}

// strongETag returns a validator suitable for If-Range.
func strongETag(header http.Header) (string, bool) {
	values := header.Values("ETag")
	if len(values) != 1 {
		return "", false
	}
	tag := strings.TrimSpace(values[0])
	if len(tag) < 2 || tag[0] != '"' || tag[len(tag)-1] != '"' {
		return "", false
	}
	for i := 1; i < len(tag)-1; i++ {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Inspect raw headers and confirm whether the server really sent an inverted range.
  2. Fix the server/proxy producing the swapped offsets.
  3. Retry the download to rule out corruption.
  4. If the endpoint is unreliable, use a single full GET instead of partial downloads.

Example fix

// before
// Content-Range: bytes 200-100/500
// after
// Content-Range: bytes 100-200/500
Defensive patterns

Strategy: retry

Validate before calling

func orderedRange(h string) bool {
	_, spec, _ := strings.Cut(h, " ")
	startEnd, _, _ := strings.Cut(spec, "/")
	s, e, _ := strings.Cut(startEnd, "-")
	start, err1 := strconv.ParseInt(s, 10, 64)
	end, err2 := strconv.ParseInt(e, 10, 64)
	return err1 == nil && err2 == nil && start <= end
}

Try / catch

cr, err := parseContentRange(header)
if err != nil {
	// inverted range is likely transient corruption: retry once, then fall back
	if retry < maxRetries { return retryDownload(ctx, url, retry+1) }
	return downloadFull(ctx, url)
}

Prevention

When it happens

Trigger: openPartial or openNext received e.g. 'bytes 200-100/500'.

Common situations: Buggy server implementations swapping offsets; proxies or caching layers corrupting range values; logic errors in mock/test servers.

Related errors


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