{"record":{"id":"ca350ec94d62e56a","repo":"microsoft/aspire","slug":"target-endpoint-targetendpoint-endpointname-on-resource","errorCode":null,"errorMessage":"Target endpoint '{targetEndpoint.EndpointName}' on resource '{targetEndpoint.Resource.Name}' has already been added to dev tunnel '{tunnel.Name}'.","messagePattern":"Target endpoint '(.+?)' on resource '(.+?)' has already been added to dev tunnel '(.+?)'\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs","lineNumber":578,"sourceCode":"                    }\n                }\n            });\n\n        return builder;\n    }\n\n    private static void AddDevTunnelPort(\n        IResourceBuilder<DevTunnelResource> tunnelBuilder,\n        EndpointReference targetEndpoint,\n        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        }","sourceCodeStart":560,"sourceCodeEnd":596,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs#L560-L596","documentation":"AddDevTunnelPort prevents the same endpoint reference from being added twice to one dev tunnel. It checks the tunnel's existing Ports for a port whose TargetEndpoint equals the given reference and throws ArgumentException naming the 'targetEndpoint' parameter if found. Each endpoint on a resource can only map to one tunnel port per tunnel.","triggerScenarios":"Calling WithReference(tunnelBuilder, endpoint) — or chaining WithReference for the same endpoint — more than once against the same tunnel, e.g. WithReference(tunnel, api.GetEndpoint(\"https\")) appearing twice, or an endpoint being routed through multiple WithReference calls.","commonSituations":"Copy-pasted builder chains adding the same endpoint twice; a shared extension method that also adds the endpoint to a tunnel the user already wired; accidentally passing the same EndpointReference instead of two different endpoints.","solutions":["Remove the duplicate WithReference call that passes the same endpoint to the tunnel.","If multiple ports are needed, expose distinct EndpointAnnotation endpoints on the target resource and reference each once.","Check the tunnel builder chain for shared helper methods that may already have added the endpoint."],"exampleFix":"// before\nvar tunnel = builder.AddDevTunnel(\"t\")\n    .WithReference(api.GetEndpoint(\"https\"))\n    .WithReference(api.GetEndpoint(\"https\")); // duplicate\n// after\nvar tunnel = builder.AddDevTunnel(\"t\")\n    .WithReference(api.GetEndpoint(\"https\"));","handlingStrategy":"validation","validationCode":"// ensure each endpoint is referenced at most once per tunnel\nvar seen = new HashSet<string>();\nvoid AddRef(Aspire.Hosting.ApplicationModel.IResourceBuilder<DevTunnelResource> t, Aspire.Hosting.ApplicationModel.EndpointReference e)\n{\n    if (!seen.Add($\"{t.Resource.Name}:{e.EndpointName}:{e.Resource.Name}\"))\n        throw new InvalidOperationException($\"Endpoint {e.EndpointName} already added to tunnel {t.Resource.Name}\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep tunnel wiring in one place; avoid multiple helper methods adding the same endpoint","Search builder chains for duplicated WithReference calls on the same endpoint","Prefer distinct named endpoints per tunneled service"],"tags":["devtunnels","duplicate","endpoint","argument"],"backgroundTag":"duplicate-entry","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"}