netbirdio/netbird · error

invalid port %s: %w

Error message

invalid port %s: %w

What it means

Produced inside validateDestinationPort when the port segment of a destination address splits cleanly but is not a base-10 integer — strconv.Atoi fails on it. This rejects service names (http, ssh), empty strings, trailing whitespace, and any non-numeric characters; a leading + or - is accepted by Atoi, so `-1` fails later in the range check instead.

Source

Thrown at client/cmd/ssh.go:689

	return nil
}

// validateDestinationPort checks that the destination address has a valid port.
// Port 0 is only valid for bind addresses (where the OS picks an available port),
// not for destination addresses where we need to connect.
func validateDestinationPort(addr string) error {
	if strings.HasPrefix(addr, "/") || strings.HasPrefix(addr, "./") {
		return nil
	}

	_, portStr, err := net.SplitHostPort(addr)
	if err != nil {
		return fmt.Errorf("parse address %s: %w", addr, err)
	}

	port, err := strconv.Atoi(portStr)
	if err != nil {
		return fmt.Errorf("invalid port %s: %w", portStr, err)
	}

	if port == 0 {
		return fmt.Errorf("port 0 is not valid for destination address")
	}

	if port < 0 || port > 65535 {
		return fmt.Errorf("port %d out of range (1-65535)", port)
	}

	return nil
}

// parsePortForwardSpec parses port forward specifications like "8080:localhost:80" or "[::1]:8080:localhost:80".
// Also supports Unix sockets like "8080:/tmp/socket" or "127.0.0.1:8080:/tmp/socket".
func parsePortForwardSpec(spec string) (string, string, error) {
	// Support formats:
	// port:host:hostport  -> localhost:port -> host:hostport

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Replace the service name with its numeric port: https -> 443, http -> 80, ssh -> 22.
  2. Strip protocol suffixes and whitespace from scripted inputs (`${PORT%%/*}` and trims).
  3. Echo the final spec when it is variable-built to catch empty port segments before invoking netbird ssh.

Example fix

# before
netbird ssh -L 8443:intranet:https peer1
# -> invalid remote address: invalid port https: strconv.Atoi: parsing "https": invalid syntax

# after
netbird ssh -L 8443:intranet:443 peer1
Defensive patterns

Strategy: validation

Validate before calling

// accept only numeric ports, resolve names once at config load
svcPorts := map[string]int{"http": 80, "https": 443, "ssh": 22, "postgres": 5432}
func normalizePort(p string) (int, error) {
	p = strings.TrimSpace(strings.TrimSuffix(strings.TrimSuffix(p, "/tcp"), "/udp"))
	if n, ok := svcPorts[p]; ok {
		return n, nil
	}
	n, err := strconv.Atoi(p)
	if err != nil {
		return 0, fmt.Errorf("port %q must be numeric", p)
	}
	return n, nil
}

Type guard

func isNumericPort(s string) bool {
	n, err := strconv.Atoi(s)
	return err == nil && n >= 1 && n <= 65535
}

Try / catch

if _, err := strconv.Atoi(portStr); err != nil {
	// *strconv.NumError: map service names via getservent/your table,
	// or reject with a message that names the offending segment
}

Prevention

When it happens

Trigger: Destination `host:https`, `host:` (empty port), `host: 80` (space), or `host:80tcp`. Reached through `invalid remote address: invalid port ...` (-L) or `invalid local address: invalid port ...` (-R).

Common situations: Using IANA service names from muscle memory; copy-paste artifacts like `80,` or `80;`; CI variables containing a port plus protocol suffix (80/tcp); locales/keyboards inserting a non-breaking space.

Related errors


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