XTLS/Xray-core · error

invalid range: %d-%d

Error message

invalid range: %d-%d

What it means

paddingTurnBounds validates a range-based (non-variant) padding turn: minLength must be >= 1, maxLength must be >= minLength, and maxLength must not exceed maxPaddingTurnLength (8 MiB, padding.go:20). A violation means the configured padding length range is empty, inverted, or oversized, and the scheduler cannot bound the record lengths it generates.

Source

Thrown at transport/internet/finalmask/xmc/padding.go:324

		copy(delays, variant.delays[firstChunk:])
	}
	return chunks, delays, nil
}

func defaultPaddingChunks(recordLength, writeChunkLength int) []int {
	chunks := make([]int, 0, (recordLength+writeChunkLength-1)/writeChunkLength)
	for remaining := recordLength; remaining > 0; {
		chunkLength := min(remaining, writeChunkLength)
		chunks = append(chunks, chunkLength)
		remaining -= chunkLength
	}
	return chunks
}

func paddingTurnBounds(turn paddingTurn) (int, int, error) {
	if len(turn.variants) == 0 {
		if turn.minLength < 1 || turn.maxLength < turn.minLength || turn.maxLength > maxPaddingTurnLength {
			return 0, 0, fmt.Errorf("invalid range: %d-%d", turn.minLength, turn.maxLength)
		}
		return turn.minLength, turn.maxLength, nil
	}
	if turn.minLength != 0 || turn.maxLength != 0 {
		return 0, 0, fmt.Errorf("variants cannot be combined with a length range")
	}

	minLength := maxPaddingTurnLength + 1
	maxLength := 0
	for i, variant := range turn.variants {
		if len(variant.chunks) == 0 {
			return 0, 0, fmt.Errorf("variant %d has no chunks", i)
		}
		if len(variant.delays) != 0 && len(variant.delays) != len(variant.chunks) {
			return 0, 0, fmt.Errorf("variant %d has %d chunks and %d delays", i, len(variant.chunks), len(variant.delays))
		}
		for j, chunkLength := range variant.chunks {
			if chunkLength < 1 || chunkLength > maxPaddingChunkLength {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Set minLength >= 1 and maxLength >= minLength, both <= 8388608.
  2. To minimize padding, use a small valid range like 1-16 instead of 0-0.
  3. If you need turns larger than 8 MiB, split them into multiple consecutive turns within the cap.
  4. Check the exact values in the error text: it prints the offending minLength-maxLength pair.

Example fix

// before
turn := paddingTurn{minLength: 0, maxLength: 0}
// after
turn := paddingTurn{minLength: 1, maxLength: 16}
Defensive patterns

Strategy: validation

Validate before calling

const maxPaddingTurnLength = 8 * 1024 * 1024

func validRange(min, max int) bool {
    return min >= 1 && max >= min && max <= maxPaddingTurnLength
}
// before config load:
if !validRange(turn.MinLength, turn.MaxLength) { return fmt.Errorf("bad padding range %d-%d", turn.MinLength, turn.MaxLength) }

Prevention

When it happens

Trigger: Building a paddingTurn with MinLength/MaxLength such as {0, 0}, {500, 100}, or {1, 9000000}; happens in clientPlayPaddingTurn/serverPlayPaddingTurn or hand-rolled schedules passed to validatePaddingSchedule.

Common situations: Copying a config snippet and editing the padding range to 0 to 'disable' padding (should use a valid 1-byte minimum instead); unit confusion (bits vs bytes); trying a very large decoy length above the 8 MiB cap.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/7de2cf80113adf28. Report an issue: GitHub.