{"record":{"id":"80569c3f542eb4a7","repo":"microsoft/aspire","slug":"invalid-protocol-proto-specified-in-port-options-supported","errorCode":null,"errorMessage":"Invalid protocol '{proto}' specified in port options. Supported protocols are 'http', 'https', or 'auto'. Set protocol to null to use the endpoint's scheme.","messagePattern":"Invalid protocol '(.+?)' specified in port options\\. Supported protocols are 'http', 'https', or 'auto'\\. Set protocol to null to use the endpoint's scheme\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs","lineNumber":595,"sourceCode":"            // 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);\n        portOptions.Labels.Add(targetEndpoint.EndpointName);\n\n        if (!TryValidateLabels(portOptions.Labels, out var errorMessage))\n        {\n            throw new ArgumentException(errorMessage, nameof(portOptions));\n        }\n","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs#L577-L613","documentation":"When portOptions.Protocol is explicitly supplied to AddDevTunnelPort it must be 'http', 'https', or 'auto'; anything else throws ArgumentException naming portOptions. Passing null is allowed and means 'infer from the endpoint scheme'. Dev tunnels need a valid protocol to declare for the forwarded port.","triggerScenarios":"Calling AddDevTunnelPort/WithReference tunnel configuration with PortOptions such as p => p.Protocol = \"tcp\" or a typo like \"HTTP\"/\"htps\" — any value outside http/https/auto.","commonSituations":"Copy-pasting protocol values intended for Docker or Kestrel configs ('tcp', 'Http'); typos; assuming case-insensitivity that the check does not perform.","solutions":["Set portOptions.Protocol to one of 'http', 'https', or 'auto' (exact lowercase spelling).","Leave Protocol null so it defaults from the endpoint's scheme.","Use 'auto' when you want the tunnel to negotiate the protocol."],"exampleFix":"// before\nbuilder.AddDevTunnel(\"t\").WithReference(api.GetEndpoint(\"https\"), p => p.Protocol = \"tcp\");\n// after\nbuilder.AddDevTunnel(\"t\").WithReference(api.GetEndpoint(\"https\"), p => p.Protocol = \"https\");","handlingStrategy":"validation","validationCode":"// validate protocol before building\nvar allowed = new[] { \"http\", \"https\", \"auto\" };\nif (portOptions.Protocol is { } p && !allowed.Contains(p))\n    throw new ArgumentException($\"Protocol '{p}' invalid; use http/https/auto or null\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use string constants instead of hand-typed literals for Protocol","Prefer leaving Protocol null to inherit the endpoint scheme","Remember matching is exact lowercase"],"tags":["devtunnels","protocol","port-options","validation"],"backgroundTag":"invalid-enum-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"}