XTLS/Xray-core · error
read byte: %w
Error message
read byte: %w
What it means
readByte performs io.ReadFull for a single byte and wraps failures. Because it reads only 1 byte, io.EOF here means the peer closed the stream exactly at a packet boundary (clean close), while io.ErrUnexpectedEOF/reset means truncation or abrupt disconnect. Most protocol field reads bottom out in this function.
Source
Thrown at transport/internet/finalmask/xmc/protocol.go:372
length := Varint(len(*v))
err := length.writeTo(w)
if err != nil {
return fmt.Errorf("write bytes length: %w", err)
}
_, err = w.Write(*v)
if err != nil {
return fmt.Errorf("write bytes: %w", err)
}
return nil
}
func readByte(r io.Reader) (byte, error) {
var buf [1]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return 0, fmt.Errorf("read byte: %w", err)
}
return buf[0], nil
}
func writePacket(w io.Writer, packetID int, fields ...field) error {
_, err := writePacketWithLength(w, packetID, fields...)
return err
}
func writePacketWithLength(w io.Writer, packetID int, fields ...field) (int, error) {
frame, err := encodePacket(packetID, fields...)
if err != nil {
return 0, err
}
if err = writeFull(w, frame); err != nil {
return 0, fmt.Errorf("write packet data: %w", err)
}View on GitHub (pinned to 7d214f8b09)
Solutions
- Special-case io.EOF: if it arrives at a packet boundary it is a graceful close — exit cleanly instead of logging an error.
- For resets/truncation, reconnect with backoff at the session layer.
- Keep TCP keepalives enabled so dead peers are detected.
- Correlate with server-side logs to see who closed first.
Example fix
// before
if err := field.readFrom(r); err != nil { log.Error(err) }
// after
if err := field.readFrom(r); err != nil {
if errors.Is(err, io.EOF) { return nil }
return err
} Defensive patterns
Strategy: try-catch
Try / catch
b, err := readByte(r)
if err != nil {
if errors.Is(err, io.EOF) {
return io.EOF // clean close at packet boundary: exit normally
}
if errors.Is(err, io.ErrUnexpectedEOF) {
return fmt.Errorf("peer truncated mid-field: %w", err)
}
return err
} Prevention
- Handle io.EOF from field reads as a normal session end, not an error.
- Keep TCP keepalives on so dead peers are detected by the runtime, not mid-parse.
- Correlate client-side occurrences with server logs to identify who closed first.
When it happens
Trigger: Any field-level read on a closed or truncated connection: peer disconnect between packets, RST from firewall, read deadline expiry at a field start.
Common situations: Normal session end surfacing as this error during shutdown; server kicking the client (bad credentials, duplicate login); idle NAT timeout killing the connection.
Related errors
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/8d17ea09609c5673.
Report an issue: GitHub.