{"record":{"id":"02611ba2ce677fa3","repo":"XTLS/Xray-core","slug":"failed-to-dial-to-dest","errorCode":null,"errorMessage":"failed to dial to ${dest}","messagePattern":"failed to dial to (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"proxy/trojan/server.go","lineNumber":463,"sourceCode":"\tfb := pfb[path]\n\tif fb == nil {\n\t\treturn errors.New(`failed to find the default \"path\" config`).AtWarning()\n\t}\n\n\tctx, cancel := context.WithCancel(ctx)\n\ttimer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)\n\tctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)\n\n\tvar conn net.Conn\n\tif err := retry.ExponentialBackoff(5, 100).On(func() error {\n\t\tvar dialer net.Dialer\n\t\tconn, err = dialer.DialContext(ctx, fb.Type, fb.Dest)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\treturn errors.New(\"failed to dial to \" + fb.Dest).Base(err).AtWarning()\n\t}\n\tdefer conn.Close()\n\n\tserverReader := buf.NewReader(conn)\n\tserverWriter := buf.NewWriter(conn)\n\n\tpostRequest := func() error {\n\t\tdefer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)\n\t\tif fb.Xver != 0 {\n\t\t\tipType := 4\n\t\t\tremoteAddr, remotePort, err := net.SplitHostPort(connection.RemoteAddr().String())\n\t\t\tif err != nil {\n\t\t\t\tipType = 0\n\t\t\t}\n\t\t\tlocalAddr, localPort, err := net.SplitHostPort(connection.LocalAddr().String())\n\t\t\tif err != nil {\n\t\t\t\tipType = 0\n\t\t\t}","sourceCodeStart":445,"sourceCodeEnd":481,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/trojan/server.go#L445-L481","documentation":"Dialing the fallback destination failed after retry.ExponentialBackoff(5, 100ms) exhausted all attempts (dialer.DialContext(fb.Type, fb.Dest)). fb.Dest is a string address; fb.Type is typically 'tcp' or 'unix'. Marked AtWarning because the original trojan failure is already logged.","triggerScenarios":"The fallback dest service is down, the address/port in the fallback config is wrong, a unix socket path does not exist, or the local firewall blocks the loopback connection.","commonSituations":"Nginx/web server behind the fallback not running, dest typo ('127.0.0.1:80' vs 'localhost:443'), SELinux/AppArmor blocking dials, or unix socket permission mismatch after privilege drop.","solutions":["Confirm the fallback backend is listening: ss -tlnp | grep <port> from the xray host","Correct the dest string in the fallback config (and type: 'unix' needs an absolute socket path)","Check local firewall/SELinux for the xray process's right to dial that dest","For unix sockets, ensure file permissions allow the xray user"],"exampleFix":"// json: point fallback at a verified listener\n\"fallbacks\": [{\"dest\": \"127.0.0.1\", \"port\": 8080, \"xver\": 0}]","handlingStrategy":"retry","validationCode":"// preflight the fallback target before enabling it\nfunc destAlive(network, addr string) bool {\n    c, err := net.DialTimeout(network, addr, time.Second)\n    if err != nil { return false }\n    c.Close()\n    return true\n}","typeGuard":null,"tryCatchPattern":"if err := retry.ExponentialBackoff(5, 100).On(dialFallback); err != nil {\n    // backing off already happened; surface config/infra problem, do not hot-loop\n    return errors.New(\"failed to dial to \" + fb.Dest).Base(err).AtWarning()\n}","preventionTips":["Health-check fallback backends alongside xray itself (systemd dependencies, upstream checks)","Use 127.0.0.1:port rather than localhost to avoid IPv6-resolution surprises","For unix sockets, confirm permissions survive xray's privilege drop"],"tags":["trojan","fallback","dial"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}