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
- Set minLength >= 1 and maxLength >= minLength, both <= 8388608.
- To minimize padding, use a small valid range like 1-16 instead of 0-0.
- If you need turns larger than 8 MiB, split them into multiple consecutive turns within the cap.
- 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
- Clamp user-supplied padding ranges to [1, 8388608] at config load.
- Never use 0-0 to disable padding; use a 1-byte minimum turn instead.
- Document the 8 MiB cap next to the config field.
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
- variants cannot be combined with a length range
- variant %d has no chunks
- empty padding schedule
- negative first turn prefix length: %d
- first prefixed padding turn is not client-to-server
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/7de2cf80113adf28.
Report an issue: GitHub.