{"record":{"id":"a7f0a2493f935967","repo":"XTLS/Xray-core","slug":"read-padding-turn-d-w","errorCode":null,"errorMessage":"read padding turn %d: %w","messagePattern":"read padding turn (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"transport/internet/finalmask/xmc/padding.go","lineNumber":67,"sourceCode":"\t\treturn err\n\t}\n\n\tvar writeBuffer []byte\n\tfor i, turn := range schedule {\n\t\tprefixLength := 0\n\t\tif i == 0 {\n\t\t\tprefixLength = firstTurnPrefixLength\n\t\t}\n\n\t\tlocalSends := isClient == (turn.direction == paddingClientToServer)\n\t\tif localSends {\n\t\t\tif err := writePaddingTurnWithBuffer(writer, turn, prefixLength, time.Sleep, &writeBuffer); err != nil {\n\t\t\t\treturn fmt.Errorf(\"write padding turn %d: %w\", i, err)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif err := readPaddingTurn(reader, turn, prefixLength); err != nil {\n\t\t\treturn fmt.Errorf(\"read padding turn %d: %w\", i, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validatePaddingSchedule(schedule []paddingTurn, firstTurnPrefixLength int) error {\n\tif len(schedule) == 0 {\n\t\treturn fmt.Errorf(\"empty padding schedule\")\n\t}\n\tif firstTurnPrefixLength < 0 {\n\t\treturn fmt.Errorf(\"negative first turn prefix length: %d\", firstTurnPrefixLength)\n\t}\n\tif firstTurnPrefixLength > 0 && schedule[0].direction != paddingClientToServer {\n\t\treturn fmt.Errorf(\"first prefixed padding turn is not client-to-server\")\n\t}\n\n\tfor i, turn := range schedule {\n\t\tif turn.direction != paddingClientToServer && turn.direction != paddingServerToClient {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/transport/internet/finalmask/xmc/padding.go#L49-L85","documentation":"runPaddingSchedule failed while reading the peer's padding turn i: the record length header could not be read, the length was implausible (<header size or >8 MiB), the total fell outside the turn's accepted range/variant set, or the body read hit EOF. In practice this means the peer's padding does not match what this side expects.","triggerScenarios":"Client and server padding schedules differ (version mismatch); peer is not an xmc endpoint so its bytes are not padding records; connection drops mid-padding so readPaddingTurn gets EOF/partial data.","commonSituations":"Mixing library versions where newClientPaddingSchedule2612 changed; pointing the transport at a real Minecraft server or random service; middlebox truncating the handshake; test harnesses feeding arbitrary bytes.","solutions":["Confirm both ends run the same xmc version so padding schedules match","Verify the remote address is the xmc counterpart, not a vanilla Minecraft or HTTP server","Check for full-stream connectivity (the wrapped error often is a read timeout or EOF)","Treat as fatal: close the connection and retry the dial"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := runPaddingSchedule(...)\nif err != nil && strings.Contains(err.Error(), \"read padding turn\") {\n    // peer padding mismatch: wrong version or wrong endpoint; abort dial\n    return err\n}","preventionTips":["Match xmc versions on client and server","Health-check the remote endpoint before relying on the tunnel","Retain packet captures of the padding phase to debug schedule mismatches"],"tags":["go","network","padding","handshake","traffic-shaping"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}