{"record":{"id":"e03cbacb62da374e","repo":"MHSanaei/3x-ui","slug":"connection-has-no-tcp-addresses","errorCode":null,"errorMessage":"connection has no TCP addresses","messagePattern":"connection has no TCP addresses","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/web/service/reality_scan.go","lineNumber":414,"sourceCode":"\t\t\t\treturn -1\n\t\t\t}\n\t\t\treturn 1\n\t\t}\n\t\treturn a.LatencyMs - b.LatencyMs\n\t})\n}\n\n// writeProxyProtocolHeader emits a PROXY protocol header describing the local\n// connection so a target that requires it (Nginx `proxy_protocol`, matching a\n// REALITY inbound's xver) accepts the probe instead of resetting it. xver 1\n// sends the human-readable v1 header; xver 2 sends the binary v2 header. The\n// addresses come from the already-dialed connection, so they are always a\n// consistent, real (src, dst) pair.\nfunc writeProxyProtocolHeader(conn net.Conn, xver int) error {\n\tlocal, lok := conn.LocalAddr().(*net.TCPAddr)\n\tremote, rok := conn.RemoteAddr().(*net.TCPAddr)\n\tif !lok || !rok {\n\t\treturn fmt.Errorf(\"connection has no TCP addresses\")\n\t}\n\tif xver >= 2 {\n\t\treturn writeProxyProtocolV2(conn, local, remote)\n\t}\n\treturn writeProxyProtocolV1(conn, local, remote)\n}\n\nfunc writeProxyProtocolV1(conn net.Conn, local, remote *net.TCPAddr) error {\n\tfam := \"TCP4\"\n\tif local.IP.To4() == nil || remote.IP.To4() == nil {\n\t\tfam = \"TCP6\"\n\t}\n\theader := fmt.Sprintf(\"PROXY %s %s %s %d %d\\r\\n\", fam, local.IP.String(), remote.IP.String(), local.Port, remote.Port)\n\t_, err := conn.Write([]byte(header))\n\treturn err\n}\n\nfunc writeProxyProtocolV2(conn net.Conn, local, remote *net.TCPAddr) error {","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/service/reality_scan.go#L396-L432","documentation":"Returned by writeProxyProtocolHeader when the already-dialed connection's LocalAddr or RemoteAddr does not type-assert to *net.TCPAddr. PROXY protocol headers must describe a real (src, dst) TCP pair, so a non-TCP or already-replaced address makes header generation impossible. In practice the scanner dials TCP, so this fires mainly on wrapped/mock connections or Unix-domain fallbacks.","triggerScenarios":"The conn passed in is a net.Conn wrapper whose LocalAddr()/RemoteAddr() return a non-*net.TCPAddr type (e.g. UnixAddr, or a custom conn from a test double); the underlying socket was converted to a non-TCP transport.","commonSituations":"Unit tests injecting fake connections into the REALITY scanner; future refactors that route the probe through a socket redialer or an in-memory pipe; scanning code path changed to dial via a Unix-socket outbound.","solutions":["Ensure the REALITY scan probe always dials with net.Dialer (TCP) before writeProxyProtocolHeader is called","If testing, provide a fake conn whose LocalAddr/RemoteAddr return *net.TCPAddr values","Skip proxy-protocol header emission (treat as non-fatal) when the address family cannot be determined, since the probe can still run without xver"],"exampleFix":"// before\nif err := writeProxyProtocolHeader(conn, xver); err != nil {\n    return err\n}\n// after — probe still usable without the header\nif err := writeProxyProtocolHeader(conn, xver); err != nil {\n    logger.Warning(\"reality scan: skipping proxy header:\", err)\n}\n","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isTCPConn(conn net.Conn) bool {\n    _, lok := conn.LocalAddr().(*net.TCPAddr)\n    _, rok := conn.RemoteAddr().(*net.TCPAddr)\n    return lok && rok\n}\n","tryCatchPattern":"if err := writeProxyProtocolHeader(conn, xver); err != nil {\n    if strings.Contains(err.Error(), \"no TCP addresses\") {\n        logger.Warning(\"reality scan: cannot emit proxy header on non-TCP conn\")\n    } else {\n        return err\n    }\n}\n","preventionTips":["Always net.Dial TCP directly for REALITY probes that need proxy-protocol headers","Guard address-type assertions at the boundary instead of deep in writers","In tests, supply conns whose addresses are real *net.TCPAddr values"],"tags":["proxy-protocol","reality-scan","network","type-assertion"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}