{"record":{"id":"826cf9256c52fcab","repo":"microsoft/aspire","slug":"cannot-tunnel-endpoint-targetendpointannotation-name-with","errorCode":null,"errorMessage":"Cannot tunnel endpoint '{targetEndpointAnnotation.Name}' with host '{targetEndpointAnnotation.TargetHost}' on resource '{targetResource.Name}' because it is not a localhost endpoint.","messagePattern":"Cannot tunnel endpoint '(.+?)' with host '(.+?)' on resource '(.+?)' because it is not a localhost endpoint\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs","lineNumber":588,"sourceCode":"        DevTunnelPortOptions? portOptions)\n    {\n        var tunnel = tunnelBuilder.Resource;\n        var targetResource = targetEndpoint.Resource;\n\n        if (tunnel.Ports.FirstOrDefault(p => p.TargetEndpoint == targetEndpoint) is { } existingPort)\n        {\n            // Port already added to the tunnel for this endpoint\n            throw new ArgumentException($\"Target endpoint '{targetEndpoint.EndpointName}' on resource '{targetEndpoint.Resource.Name}' has already been added to dev tunnel '{tunnel.Name}'.\", nameof(targetEndpoint));\n        }\n\n        if (targetEndpoint.Resource.Annotations.OfType<EndpointAnnotation>()\n            .SingleOrDefault(a => string.Equals(a.Name, targetEndpoint.EndpointName, StringComparisons.EndpointAnnotationName)) is { } targetEndpointAnnotation)\n        {\n            // The target endpoint already exists so let's ensure it's target is localhost\n            if (!EndpointHostHelpers.IsLocalhostOrLocalhostTld(targetEndpointAnnotation.TargetHost))\n            {\n                // Target endpoint is not localhost so can't be tunneled\n                throw new ArgumentException($\"Cannot tunnel endpoint '{targetEndpointAnnotation.Name}' with host '{targetEndpointAnnotation.TargetHost}' on resource '{targetResource.Name}' because it is not a localhost endpoint.\", nameof(targetEndpoint));\n            }\n        }\n\n        portOptions ??= new();\n        if (portOptions.Protocol is { } proto && proto is not \"http\" and not \"https\" and not \"auto\")\n        {\n            throw new ArgumentException($\"Invalid protocol '{proto}' specified in port options. Supported protocols are 'http', 'https', or 'auto'. Set protocol to null to use the endpoint's scheme.\", nameof(portOptions));\n        }\n        portOptions.Protocol ??= targetEndpoint.Scheme switch\n        {\n            \"https\" or \"http\" => targetEndpoint.Scheme,\n            _ => throw new ArgumentException($\"Cannot tunnel endpoint '{targetEndpoint.EndpointName}' on resource '{targetResource.Name}' because it uses the unsupported scheme '{targetEndpoint.Scheme}'. Only 'http' and 'https' endpoints can be tunneled.\"),\n        };\n        portOptions.Description ??= $\"{targetResource.Name}/{targetEndpoint.EndpointName}\";\n\n        var portName = $\"{tunnel.Name}-{targetResource.Name}-{targetEndpoint.EndpointName}\";\n        portOptions.Labels ??= [];\n        portOptions.Labels.Add(targetResource.Name);","sourceCodeStart":570,"sourceCodeEnd":606,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs#L570-L606","documentation":"Dev tunnels can only forward traffic for endpoints whose target host is localhost (or the .localhost TLD). When the referenced endpoint annotation already exists on the resource and its TargetHost is not localhost, AddDevTunnelPort throws ArgumentException because tunneling a non-localhost binding is not supported. This stops configurations that would publish internal or external hostnames through a dev tunnel.","triggerScenarios":"Calling WithReference(devTunnel, endpoint) where the target resource's endpoint was configured with a non-localhost TargetHost, e.g. via WithEndpoint with a custom host, or an endpoint annotation whose TargetHost resolves to a machine name or domain rather than localhost.","commonSituations":"Resources configured for external binding during development (e.g. endpoints bound to a LAN hostname or custom domain); copied project setups where someone set the host explicitly; integrating with services that register remote endpoints.","solutions":["Change the target endpoint so its host is localhost (default) — remove custom host configuration such as WithEndpoint host overrides.","Use a different, localhost-bound endpoint on the resource as the tunnel target.","If remote forwarding is genuinely needed, use dev tunnel CLI features outside the Aspire builder instead of WithReference."],"exampleFix":"// before\nvar api = builder.AddProject<Projects.Api>(\"api\")\n    .WithEndpoint(\"https\", e => e.TargetHost = \"mybox.example.com\");\nvar tunnel = builder.AddDevTunnel(\"t\").WithReference(api.GetEndpoint(\"https\"));\n// after (keep default localhost target)\nvar api = builder.AddProject<Projects.Api>(\"api\").WithHttpsEndpoint();\nvar tunnel = builder.AddDevTunnel(\"t\").WithReference(api.GetEndpoint(\"https\"));","handlingStrategy":"validation","validationCode":"// before tunneling, confirm the endpoint host is localhost\nif (!EndpointHostHelpers.IsLocalhostOrLocalhostTld(endpoint.TargetHost))\n    throw new InvalidOperationException($\"Endpoint {endpoint.EndpointName} host '{endpoint.TargetHost}' is not localhost; cannot tunnel\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not override TargetHost on endpoints that will be tunneled","Keep tunneled endpoints on their default localhost binding","Document that dev tunnels only forward localhost-bound endpoints"],"tags":["devtunnels","localhost","endpoint","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}