XTLS/Xray-core · error
negative first turn prefix length: %d
Error message
negative first turn prefix length: %d
What it means
validatePaddingSchedule rejected a negative firstTurnPrefixLength. The prefix length represents bytes of the real protocol record already sent before padding begins; it can be zero or positive but never negative. Only a caller bug can produce a negative value.
Source
Thrown at transport/internet/finalmask/xmc/padding.go:78
if localSends {
if err := writePaddingTurnWithBuffer(writer, turn, prefixLength, time.Sleep, &writeBuffer); err != nil {
return fmt.Errorf("write padding turn %d: %w", i, err)
}
continue
}
if err := readPaddingTurn(reader, turn, prefixLength); err != nil {
return fmt.Errorf("read padding turn %d: %w", i, err)
}
}
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)
}View on GitHub (pinned to 7d214f8b09)
Solutions
- Audit the call site that computes firstTurnPrefixLength and clamp/assert it is >= 0
- Track bytes written with a counter rather than computing by subtraction
- Add a unit test covering the zero-prefix and positive-prefix cases
Example fix
// before
prefix := int(writtenHeader) - int(sentBytes)
// after
prefix := sentBytes - writtenHeader
if prefix < 0 {
return fmt.Errorf("negative prefix: %d", prefix)
} Defensive patterns
Strategy: validation
Validate before calling
if prefix < 0 {
return fmt.Errorf("invalid prefix length %d", prefix)
} Type guard
func validPrefixLength(n int) bool { return n >= 0 } Prevention
- Compute the prefix from a bytes-written counter, never by subtraction
- Reject negative prefix values at the API boundary
When it happens
Trigger: Passing a negative prefix length to runPaddingSchedule; computing the prefix as 'bytes remaining' minus 'bytes sent' with the operands swapped so it goes negative.
Common situations: Forks that derive firstTurnPrefixLength arithmetically instead of tracking bytes actually written; test code passing -1 as a sentinel.
Related errors
- empty padding schedule
- first prefixed padding turn is not client-to-server
- padding turn %d has invalid direction: %d
- padding turn %d has an invalid start delay: %w
- padding turn %d has an invalid chunk delay: %w
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/547dea020f412f1b.
Report an issue: GitHub.