XTLS/Xray-core · error
padding turn %d has invalid direction: %d
Error message
padding turn %d has invalid direction: %d
What it means
validatePaddingSchedule found a turn whose direction field is neither paddingClientToServer (1) nor paddingServerToClient (2) — i.e. zero (unset) or an out-of-range value. Padding turns must alternate directions, so an unset or corrupted direction fails immediately.
Source
Thrown at transport/internet/finalmask/xmc/padding.go:86
}
}
return nil
}
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)
}View on GitHub (pinned to 7d214f8b09)
Solutions
- Set direction explicitly on every turn using the paddingClientToServer/paddingServerToClient constants
- Build schedules with a constructor that alternates directions automatically
- Run validatePaddingSchedule in unit tests for every schedule your code ships
Example fix
// before
turn := paddingTurn{minLength: 200, maxLength: 400} // direction unset (0)
// after
turn := paddingTurn{direction: paddingClientToServer, minLength: 200, maxLength: 400} Defensive patterns
Strategy: type-guard
Type guard
func validPaddingDirection(d paddingDirection) bool {
return d == paddingClientToServer || d == paddingServerToClient
} Prevention
- Always set direction explicitly using the package constants
- Generate alternating directions with a builder helper
When it happens
Trigger: A paddingTurn literal where direction was never assigned (defaults to 0), or arithmetic on paddingDirection that overflows the two valid constants.
Common situations: Hand-written schedule literals in forks; omitting the Direction field when constructing turns from config structs; refactors that renamed the constants.
Related errors
- first prefixed padding turn is not client-to-server
- padding turn %d combines variants with generated write chunk
- padding turn %d: %w
- unexpected client IP length
- empty padding schedule
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/f779f02afb39e0d4.
Report an issue: GitHub.