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
- Remove --with-external-port when using http/https; the cluster assigns the URL
- 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
- Only add --with-external-port when the protocol is tcp, udp, or tls
- In wrappers, build the flag list conditionally per protocol instead of reusing one template
- HTTP(S) exposures get their URL from the cluster; a fixed port is not a thing there
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
- invalid port number: %s
- invalid port number: must be between 1 and 65535
- auth flags (--with-pin, --with-password, --with-user-groups)
- unsupported protocol %q: must be http, https, tcp, udp, or t
- invalid pin: must be exactly 6 digits
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/e0b4eecf9f0f61be.
Report an issue: GitHub.