XTLS/Xray-core · error

padding turn %d has an invalid start delay: %w

Error message

padding turn %d has an invalid start delay: %w

What it means

validatePaddingSchedule rejected turn i's startDelay because its min is negative or max < min (via validatePaddingDelayRange). Start delays pace how long the endpoint waits before beginning a padding turn; they must satisfy 0 <= min <= max.

Source

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

}

func validatePaddingSchedule(schedule []paddingTurn, firstTurnPrefixLength int) error {
	if len(schedule) == 0 {
		return fmt.Errorf("empty padding schedule")
	}
	if firstTurnPrefixLength < 0 {
		return fmt.Errorf("negative first turn prefix length: %d", firstTurnPrefixLength)
	}
	if firstTurnPrefixLength > 0 && schedule[0].direction != paddingClientToServer {
		return fmt.Errorf("first prefixed padding turn is not client-to-server")
	}

	for i, turn := range schedule {
		if turn.direction != paddingClientToServer && turn.direction != paddingServerToClient {
			return fmt.Errorf("padding turn %d has invalid direction: %d", i, turn.direction)
		}
		if err := validatePaddingDelayRange(turn.startDelay); err != nil {
			return fmt.Errorf("padding turn %d has an invalid start delay: %w", i, err)
		}
		if err := validatePaddingDelayRange(turn.chunkDelay); err != nil {
			return fmt.Errorf("padding turn %d has an invalid chunk delay: %w", i, err)
		}
		if turn.writeChunkMinLength < 0 || turn.writeChunkLength < turn.writeChunkMinLength || turn.writeChunkLength > maxPaddingChunkLength {
			return fmt.Errorf("padding turn %d has an invalid write chunk range: %d-%d", i, turn.writeChunkMinLength, turn.writeChunkLength)
		}
		if len(turn.variants) > 0 && turn.writeChunkLength != 0 {
			return fmt.Errorf("padding turn %d combines variants with generated write chunks", i)
		}

		minLength, maxLength, err := paddingTurnBounds(turn)
		if err != nil {
			return fmt.Errorf("padding turn %d: %w", i, err)
		}
		hasSendRange := turn.sendMinLength != 0 || turn.sendMaxLength != 0
		if hasSendRange {
			if len(turn.variants) > 0 {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Correct the range so 0 <= startDelay.min <= startDelay.max
  2. If delays come from user config, clamp or reject negative values at parse time
  3. Use time.Duration literals (e.g. 10*time.Millisecond) instead of raw integers

Example fix

// before
startDelay: paddingDelayRange{min: 50 * time.Millisecond, max: 10 * time.Millisecond}

// after
startDelay: paddingDelayRange{min: 10 * time.Millisecond, max: 50 * time.Millisecond}
Defensive patterns

Strategy: validation

Validate before calling

func validDelayRange(d paddingDelayRange) bool { return d.min >= 0 && d.max >= d.min }

Prevention

When it happens

Trigger: A schedule turn with startDelay.min negative (e.g. parsed from a signed config value) or with the min/max fields swapped so max < min.

Common situations: Forks exposing delay ranges in config with signed ints; unit tests with placeholder negative durations; refactors confusing time.Duration sign conventions.

Related errors


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