{"record":{"id":"48dff0512b0499f0","repo":"XTLS/Xray-core","slug":"insufficient-header","errorCode":null,"errorMessage":"insufficient header","messagePattern":"insufficient header","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/socks/protocol.go","lineNumber":65,"sourceCode":"\taddress      net.Address\n\tport         net.Port\n\tlocalAddress net.Address\n}\n\nfunc (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {\n\tif s.config.AuthType == AuthType_PASSWORD {\n\t\twriteSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0))\n\t\treturn nil, errors.New(\"socks 4 is not allowed when auth is required.\")\n\t}\n\n\tvar port net.Port\n\tvar address net.Address\n\n\t{\n\t\tbuffer := buf.StackNew()\n\t\tif _, err := buffer.ReadFullFrom(reader, 6); err != nil {\n\t\t\tbuffer.Release()\n\t\t\treturn nil, errors.New(\"insufficient header\").Base(err)\n\t\t}\n\t\tport = net.PortFromBytes(buffer.BytesRange(0, 2))\n\t\taddress = net.IPAddress(buffer.BytesRange(2, 6))\n\t\tbuffer.Release()\n\t}\n\n\tif _, err := ReadUntilNull(reader); /* user id */ err != nil {\n\t\treturn nil, err\n\t}\n\tif address.IP()[0] == 0x00 {\n\t\tdomain, err := ReadUntilNull(reader)\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"failed to read domain for socks 4a\").Base(err)\n\t\t}\n\t\taddress = net.ParseAddress(domain)\n\t}\n\n\tswitch cmd {","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/socks/protocol.go#L47-L83","documentation":"Thrown by ServerSession.handshake4 (proxy/socks/protocol.go:65) when reading the fixed 6-byte SOCKS4 request body (2-byte port + 4-byte IP) fails. ReadFullFrom requires exactly 6 bytes, so the error means the peer sent fewer bytes than needed and the stream ended (EOF/timeout/reset) before the header was complete.","triggerScenarios":"A client that writes the SOCKS4 version/command bytes and then closes, resets, or stalls the connection before sending the remaining 6 bytes; a scanner or health-check that only writes 2 probe bytes; a truncated client implementation.","commonSituations":"Port scanners / idle probers hitting the SOCKS port; broken or half-implemented SOCKS4 clients; network middleboxes truncating small packets; TCP connection cut right after handshake start.","solutions":["Verify the client actually speaks SOCKS4/4a and sends the full request: VER(1) CMD(1) DSTPORT(2) DSTIP(4) USERID(nul).","Check the base error: EOF means the peer closed early (client bug); timeout means a stalled or probing connection.","Reproduce with a known-good client (curl --socks4) to confirm the server config is fine; if it works, fix or replace the custom client.","If the source is a scanner, restrict inbound exposure (firewall/bind to trusted interface)."],"exampleFix":"// before: half-written SOCKS4 request\nconn.Write([]byte{0x04, 0x01, 0x00}) // only 3 bytes, then closed\n\n// after: complete SOCKS4 CONNECT header (port 80, IP 93.184.216.34, empty userid)\nconn.Write([]byte{0x04, 0x01, 0x00, 0x50, 93, 184, 216, 34, 0x00})","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"header, udpConn, err := session.Handshake(reader, conn)\nif err != nil {\n    if strings.Contains(err.Error(), \"insufficient header\") {\n        // peer sent a truncated or probe request: log at debug and close quietly\n        logDebug(\"truncated socks4 request from %v\", conn.RemoteAddr())\n    }\n    return err\n}","preventionTips":["Write complete SOCKS4 frames in a single conn.Write call.","Verify client framing with a reference implementation before shipping.","Expect scanners on any exposed SOCKS port and keep logging at a level that tolerates the noise."],"tags":["socks","protocol","network","truncated-request","xray"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}