netbirdio/netbird · warning

--with-external-port is not supported for %s protocol

Error message

--with-external-port is not supported for %s protocol

What it means

--with-external-port was explicitly set while --protocol is http or https. cmd.Flags().Changed("with-external-port") is true, but the flag only has meaning for cluster (L4) protocols: the request builder sets req.ListenPort from resolveExternalPort only when isClusterProtocol is true. HTTP(S) services are routed by hostname on the proxy cluster, so a fixed external port does not apply.

Source

Thrown at client/cmd/expose.go:119

func validateExposeFlags(cmd *cobra.Command, portStr string) (uint64, error) {
	port, err := strconv.ParseUint(portStr, 10, 32)
	if err != nil {
		return 0, fmt.Errorf("invalid port number: %s", portStr)
	}
	if port == 0 || port > 65535 {
		return 0, fmt.Errorf("invalid port number: must be between 1 and 65535")
	}

	if !isProtocolValid(exposeProtocol) {
		return 0, fmt.Errorf("unsupported protocol %q: must be http, https, tcp, udp, or tls", exposeProtocol)
	}

	if isClusterProtocol(exposeProtocol) {
		if exposePin != "" || exposePassword != "" || len(exposeUserGroups) > 0 {
			return 0, fmt.Errorf("auth flags (--with-pin, --with-password, --with-user-groups) are not supported for %s protocol", exposeProtocol)
		}
	} else if cmd.Flags().Changed("with-external-port") {
		return 0, fmt.Errorf("--with-external-port is not supported for %s protocol", exposeProtocol)
	}

	if exposePin != "" && !pinRegexp.MatchString(exposePin) {
		return 0, fmt.Errorf("invalid pin: must be exactly 6 digits")
	}

	if cmd.Flags().Changed("with-password") && exposePassword == "" {
		return 0, fmt.Errorf("password cannot be empty")
	}

	if cmd.Flags().Changed("with-user-groups") && len(exposeUserGroups) == 0 {
		return 0, fmt.Errorf("user groups cannot be empty")
	}

	return port, nil
}

func isProtocolValid(exposeProtocol string) bool {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Remove --with-external-port when using http/https; the cluster assigns the URL
  2. To pin a public port, use an L4 protocol: `netbird expose --protocol tcp --with-external-port 5433 5432`

Example fix

# before
netbird expose --protocol http --with-external-port 8081 8080

# after
netbird expose 8080
Defensive patterns

Strategy: validation

Validate before calling

cluster := map[string]bool{"tcp": true, "udp": true, "tls": true}
if externalPortChanged && !cluster[strings.ToLower(proto)] {
	log.Fatal("--with-external-port is only valid with tcp/udp/tls; http/https are routed by hostname")
}

Prevention

When it happens

Trigger: `netbird expose --protocol http --with-external-port 8081 8080`; a script template built for tcp reuse that always passes --with-external-port.

Common situations: Wanting a stable public port number in an HTTP URL; leftover flag from a previous tcp/tls command in a wrapper script.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/e0b4eecf9f0f61be. Report an issue: GitHub.