netbirdio/netbird · error
port 0 is not valid for destination address
Error message
port 0 is not valid for destination address
What it means
Returned by validateDestinationPort when the destination port parses as an integer but is exactly 0. Port 0 on a bind address means 'let the OS choose', which is meaningful only for the listening side; a destination must identify a concrete service, so 0 is rejected for the endpoint the forward connects to. This is a sentinel error with no %w wrap — the message is final.
Source
Thrown at client/cmd/ssh.go:693
// 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
// host:port:host:hostport -> host:port -> host:hostport
// [host]:port:host:hostport -> [host]:port -> host:hostport
// port:unix_socket_path -> localhost:port -> unix_socket_path
// host:port:unix_socket_path -> host:port -> unix_socket_pathView on GitHub (pinned to 93e97f4bf1)
Solutions
- Put a real service port on the destination side: 1-65535.
- If 0 was meant for the local listener, move it to the bind position (-L 0:host:80) — that side is not port-validated by this function.
- In templates/scripts, fail on unset ports instead of defaulting to 0: ${PORT:?port required}.
Example fix
# before netbird ssh -L 8080:dbhost:0 peer1 # -> invalid remote address: port 0 is not valid for destination address # after netbird ssh -L 8080:dbhost:5432 peer1
Defensive patterns
Strategy: validation
Validate before calling
// reject zero ports before they ever reach a spec
if port == 0 {
return fmt.Errorf("port 0 is only valid on the bind side; destination %q needs 1-65535", target)
}
spec := fmt.Sprintf("%d:%s:%d", bindPort, host, port) Type guard
func isValidDestinationPort(n int) bool { return n >= 1 && n <= 65535 } Try / catch
if port == 0 {
// sentinel, no wrap: handle by re-prompting for the real service port;
// do not 'fix' it to a guessed default silently
} Prevention
- Never default unset ports to 0; fail at config load (${PORT:?required}) instead.
- Remember the asymmetry: 0 may appear on the bind side (-L 0:host:80), never on the destination side.
- Encode 'destination port must be 1-65535' as a shared validation in your spec builder so all scripts inherit it.
When it happens
Trigger: `-L 8080:host:0` (remote destination port 0) surfacing as `invalid remote address: port 0 is not valid for destination address`; `-R 9000:localhost:0` (local endpoint port 0) surfacing as `invalid local address: port 0 ...`. Note `-L 0:host:80` is different: that is the local bind port and is not checked by this validator.
Common situations: Reusing an OpenSSH pattern where a zero remote port triggers dynamic/SOCKS-style allocation (not supported here); config templates with PORT=0 meaning 'unassigned'; scripts defaulting unset ports to 0.
Related errors
- invalid port %s: %w
- port %d out of range (1-65535)
- start port forwarding: %w
- local port forward %s: %w
- remote port forward %s: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/1e90bc5406bbf885.
Report an issue: GitHub.