{"record":{"id":"8d17ea09609c5673","repo":"XTLS/Xray-core","slug":"read-byte-w","errorCode":null,"errorMessage":"read byte: %w","messagePattern":"read byte: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"transport/internet/finalmask/xmc/protocol.go","lineNumber":372,"sourceCode":"\tlength := Varint(len(*v))\n\terr := length.writeTo(w)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"write bytes length: %w\", err)\n\t}\n\n\t_, err = w.Write(*v)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"write bytes: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc readByte(r io.Reader) (byte, error) {\n\tvar buf [1]byte\n\t_, err := io.ReadFull(r, buf[:])\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"read byte: %w\", err)\n\t}\n\n\treturn buf[0], nil\n}\n\nfunc writePacket(w io.Writer, packetID int, fields ...field) error {\n\t_, err := writePacketWithLength(w, packetID, fields...)\n\treturn err\n}\n\nfunc writePacketWithLength(w io.Writer, packetID int, fields ...field) (int, error) {\n\tframe, err := encodePacket(packetID, fields...)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif err = writeFull(w, frame); err != nil {\n\t\treturn 0, fmt.Errorf(\"write packet data: %w\", err)\n\t}","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/transport/internet/finalmask/xmc/protocol.go#L354-L390","documentation":"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.","triggerScenarios":"Any field-level read on a closed or truncated connection: peer disconnect between packets, RST from firewall, read deadline expiry at a field start.","commonSituations":"Normal session end surfacing as this error during shutdown; server kicking the client (bad credentials, duplicate login); idle NAT timeout killing the connection.","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."],"exampleFix":"// before\nif err := field.readFrom(r); err != nil { log.Error(err) }\n// after\nif err := field.readFrom(r); err != nil {\n    if errors.Is(err, io.EOF) { return nil }\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"b, err := readByte(r)\nif err != nil {\n    if errors.Is(err, io.EOF) {\n        return io.EOF // clean close at packet boundary: exit normally\n    }\n    if errors.Is(err, io.ErrUnexpectedEOF) {\n        return fmt.Errorf(\"peer truncated mid-field: %w\", err)\n    }\n    return err\n}","preventionTips":["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."],"tags":["go","network","io","protocol-framing"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}