{"record":{"id":"aeda165f8511344d","repo":"XTLS/Xray-core","slug":"failed-to-open-connection-to-destination","errorCode":null,"errorMessage":"failed to open connection to {destination}","messagePattern":"failed to open connection to (.+?)","errorType":"exception","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"proxy/freedom/freedom.go","lineNumber":350,"sourceCode":"\t\t\t\terrors.LogInfo(ctx, \"dialing to \", dialDest)\n\t\t\t}\n\t\t}\n\t\tif rule := h.matchFinalRule(dialDest.Network, dialDest.Address, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {\n\t\t\tblockedDest = &dialDest\n\t\t\tblockedRule = rule\n\t\t\treturn nil\n\t\t}\n\n\t\trawConn, err := dialer.Dial(ctx, dialDest)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tconn = rawConn\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn errors.New(\"failed to open connection to \", destination).Base(err)\n\t}\n\tif blockedDest != nil {\n\t\tdelay := h.blockDelay(blockedRule)\n\t\terrors.LogInfo(ctx, \"blocked target: \", *blockedDest, \", blackholing connection for \", delay)\n\t\ttimer := time.AfterFunc(delay, func() {\n\t\t\tcommon.Interrupt(input)\n\t\t\tcommon.Interrupt(output)\n\t\t\terrors.LogInfo(ctx, \"closed blackholed connection to blocked target: \", *blockedDest)\n\t\t})\n\t\tdefer timer.Stop()\n\t\tdefer common.Close(output)\n\t\tif err := buf.Copy(input, buf.Discard); err != nil {\n\t\t\treturn nil\n\t\t}\n\t\treturn nil\n\t}\n\tif h.config.ProxyProtocol > 0 && h.config.ProxyProtocol <= 2 {\n\t\tversion := byte(h.config.ProxyProtocol)","sourceCodeStart":332,"sourceCodeEnd":368,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/freedom/freedom.go#L332-L368","documentation":"The low-level dial (dialer.Dial inside the retry task) failed and the raw error is wrapped as \"failed to open connection to <destination>\". This is the generic transport-establishment failure for the freedom outbound: TCP/UDP connect, dialer timeout, or asockopt-level refusal. The original dial error is preserved via .Base(err).","triggerScenarios":"Direct outbound to an unreachable destination: connection refused, no route to host, network timeout under the policy handshake timeout, or a sockopt dialer (e.g. via dialerProxy) failing. UDP destinations can also fail here when the OS rejects sendto on a constrained network.","commonSituations":"Target firewall drops SYNs (shows as i/o timeout), IPv6 destination on an IPv4-only host, firewall-blocked egress on VPS/container, policy timeout too small for high-latency links, or a chained dialerProxy whose own dial failed first.","solutions":["Read the Base error — \"connection refused\" vs \"i/o timeout\" vs \"network is unreachable\" point to different fixes","Test reachability from the Xray host: `nc -vz <host> <port>` / `curl -v`","If IPv6 fails, use domainStrategy UseIPv4 or fix IPv6 routing","Raise policy.handshake timeout for slow links; check sockopt (tcpFastOpen, interface) validity"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"```go\nconn, err := net.DialTimeout(\"tcp\", dest.NetAddr(), 3*time.Second)\nif err != nil { /* destination unreachable; fail early with clear message */ }\nconn.Close()\n```","typeGuard":null,"tryCatchPattern":"```go\nif err := h.Process(ctx, link, dialer); err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() { /* retry or backoff */ }\n}\n```","preventionTips":["Pre-flight reachability checks for critical destinations","Use IPv4 strategy on IPv4-only hosts","Size policy.handshake to link latency"],"tags":["freedom","dial","network","xray"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}