microsoft/aspire · error · ArgumentException

Invalid protocol ' ' specified in port options. Supported…

Error message

Invalid protocol '{proto}' specified in port options. Supported protocols are 'http', 'https', or 'auto'. Set protocol to null to use the endpoint's scheme.

What it means

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.

Solutions

  1. Set portOptions.Protocol to one of 'http', 'https', or 'auto' (exact lowercase spelling).
  2. Leave Protocol null so it defaults from the endpoint's scheme.
  3. Use 'auto' when you want the tunnel to negotiate the protocol.

Example fix

// before
builder.AddDevTunnel("t").WithReference(api.GetEndpoint("https"), p => p.Protocol = "tcp");
// after
builder.AddDevTunnel("t").WithReference(api.GetEndpoint("https"), p => p.Protocol = "https");
Defensive patterns

Strategy: validation

Validate before calling

// validate protocol before building
var allowed = new[] { "http", "https", "auto" };
if (portOptions.Protocol is { } p && !allowed.Contains(p))
    throw new ArgumentException($"Protocol '{p}' invalid; use http/https/auto or null");

Prevention

When it happens

Trigger: 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.

Common situations: Copy-pasting protocol values intended for Docker or Kestrel configs ('tcp', 'Http'); typos; assuming case-insensitivity that the check does not perform.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/80569c3f542eb4a7. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs:595

            // Port already added to the tunnel for this endpoint
            throw new ArgumentException($"Target endpoint '{targetEndpoint.EndpointName}' on resource '{targetEndpoint.Resource.Name}' has already been added to dev tunnel '{tunnel.Name}'.", nameof(targetEndpoint));
        }

        if (targetEndpoint.Resource.Annotations.OfType<EndpointAnnotation>()
            .SingleOrDefault(a => string.Equals(a.Name, targetEndpoint.EndpointName, StringComparisons.EndpointAnnotationName)) is { } targetEndpointAnnotation)
        {
            // The target endpoint already exists so let's ensure it's target is localhost
            if (!EndpointHostHelpers.IsLocalhostOrLocalhostTld(targetEndpointAnnotation.TargetHost))
            {
                // Target endpoint is not localhost so can't be tunneled
                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));
            }
        }

        portOptions ??= new();
        if (portOptions.Protocol is { } proto && proto is not "http" and not "https" and not "auto")
        {
            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));
        }
        portOptions.Protocol ??= targetEndpoint.Scheme switch
        {
            "https" or "http" => targetEndpoint.Scheme,
            _ => 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."),
        };
        portOptions.Description ??= $"{targetResource.Name}/{targetEndpoint.EndpointName}";

        var portName = $"{tunnel.Name}-{targetResource.Name}-{targetEndpoint.EndpointName}";
        portOptions.Labels ??= [];
        portOptions.Labels.Add(targetResource.Name);
        portOptions.Labels.Add(targetEndpoint.EndpointName);

        if (!TryValidateLabels(portOptions.Labels, out var errorMessage))
        {
            throw new ArgumentException(errorMessage, nameof(portOptions));
        }

View on GitHub (pinned to 25830f84bd)