{"record":{"id":"29bd45c14a0040ec","repo":"BeyondDimension/SteamTools","slug":"unable-to-connect-to-host","errorCode":null,"errorMessage":"Unable to connect to {host}","messagePattern":"Unable to connect to (.+?)","errorType":"exception","errorClass":"AggregateException","httpStatus":null,"severity":"error","filePath":"src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/Services.Implementation/HttpServer/Middleware/TunnelMiddleware.cs","lineNumber":85,"sourceCode":"        var innerExceptions = new List<Exception>();\n        await foreach (var endPoint in GetUpstreamEndPointsAsync(host, cancellationToken))\n        {\n            var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);\n            try\n            {\n                using var timeoutTokenSource = new CancellationTokenSource(connectTimeout);\n                using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);\n                await socket.ConnectAsync(endPoint, linkedTokenSource.Token);\n                return new NetworkStream(socket, ownsSocket: true);\n            }\n            catch (Exception ex)\n            {\n                socket.Dispose();\n                cancellationToken.ThrowIfCancellationRequested();\n                innerExceptions.Add(ex);\n            }\n        }\n        throw new AggregateException($\"Unable to connect to {host}\", innerExceptions);\n    }\n\n    /// <summary>\n    /// 获取目标终节点\n    /// </summary>\n    /// <param name=\"host\"></param>\n    /// <param name=\"cancellationToken\"></param>\n    /// <returns></returns>\n    async IAsyncEnumerable<EndPoint> GetUpstreamEndPointsAsync(HostString host, [EnumeratorCancellation] CancellationToken cancellationToken)\n    {\n        const int HTTPS_PORT = 443;\n        var targetHost = host.Host;\n        var targetPort = host.Port ?? HTTPS_PORT;\n\n        if (IPAddress.TryParse(targetHost, out var address) == true)\n        {\n            yield return new IPEndPoint(address, targetPort);\n        }","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/BeyondDimension/SteamTools/blob/c16ffa08e03b192d23ada290c4969e77f9201f3d/src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/Services.Implementation/HttpServer/Middleware/TunnelMiddleware.cs#L67-L103","documentation":"Thrown by TunnelMiddleware when none of the resolved upstream endpoints for a host could be connected to within connectTimeout. Each per-endpoint failure is collected into innerExceptions (after honouring cancellation) and then aggregated, naming the host that could not be reached.","triggerScenarios":"Forwarding a tunneled (e.g. HTTPS CONNECT-style) request where socket.ConnectAsync fails or times out for every endpoint returned by GetUpstreamEndPointsAsync for the HostString.","commonSituations":"Target service offline; DNS for the host resolves to unreachable IPs; port 443 (HTTPS_PORT) blocked by firewall/ISP; accelerator node behind the host is down; host header malformed so resolution yields bad endpoints.","solutions":["Read innerExceptions to distinguish timeout vs connection-refused vs DNS failure.","Confirm the host resolves and its service port (typically 443) is reachable from the host machine.","Validate the Host header being forwarded is correct and not mangled by rewriting.","Switch accelerator node / route, or disable the tunnel temporarily to confirm the target itself is up."],"exampleFix":"// before\nthrow new AggregateException($\"Unable to connect to {host}\", innerExceptions);\n\n// after: include attempt count and dominant error for faster diagnosis\nvar dominant = innerExceptions.GroupBy(e => e.GetType())\n    .OrderByDescending(g => g.Count()).First().Key.Name;\nthrow new AggregateException(\n    $\"Unable to connect to {host} ({innerExceptions.Count} attempts, dominant: {dominant})\",\n    innerExceptions);","handlingStrategy":"retry","validationCode":"// Validate the host and reachability before tunnelling.\nif (!HostString.IsValid(host.Host) || host.Port is < 1 or > 65535)\n    throw new ArgumentException($\"Invalid upstream host: {host}.\");\n\nasync Task<bool> IsUpstreamReachableAsync(HostString h, CancellationToken ct)\n{\n    await foreach (var ep in GetUpstreamEndPointsAsync(h, ct))\n    {\n        using var s = new TcpClient();\n        using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);\n        cts.CancelAfter(connectTimeout);\n        try { await s.ConnectAsync(ep, cts.Token); return true; } catch { }\n    }\n    return false;\n}","typeGuard":null,"tryCatchPattern":"try { await ForwardTunnelAsync(host, ct); }\ncatch (AggregateException ex) when (ex.Message.StartsWith(\"Unable to connect to\"))\n{\n    if (attempt < maxAttempts && ex.InnerExceptions.Any(e => e is TimeoutException))\n    { await Task.Delay(backoff, ct); goto retry; }\n    // Return 502 to the client with the host name; do not leak inner exceptions.\n    context.Response.StatusCode = 502;\n    await context.Response.WriteAsync($\"Bad gateway: {host}\", ct);\n}","preventionTips":["Configure a short connectTimeout so dead upstreams fail fast and trigger failover.","Maintain an allowlist of healthy upstream endpoints and refresh it periodically.","Return a clean 502/503 to clients instead of letting the AggregateException surface.","Monitor connect-failure rates per host to catch degraded upstreams early."],"tags":["network","tunnel","proxy","connection"],"backgroundTag":null,"analyzedSha":"c16ffa08e03b192d23ada290c4969e77f9201f3d","analyzedAt":"2026-08-13T11:52:20.410Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}