netbirdio/netbird · error
invalid port forward specification: %s
Error message
invalid port forward specification: %s
What it means
Returned by parsePortForwardSpec's default branch when the spec splits into more than four colon-separated parts. Valid specs have 2, 3, or 4 parts (port:socket, port:host:port / host:port:socket, host:port:host:port); five or more tokens have no handler. IPv6-looking specs with many colons land here unless they start with '[' and contain ']:' to be routed to the IPv6 parser.
Source
Thrown at client/cmd/ssh.go:730
if strings.HasPrefix(spec, "[") && strings.Contains(spec, "]:") {
return parseIPv6ForwardSpec(spec)
}
parts := strings.Split(spec, ":")
if len(parts) < 2 {
return "", "", fmt.Errorf("invalid port forward specification: %s (expected format: [local_host:]local_port:remote_target)", spec)
}
switch len(parts) {
case 2:
return parseTwoPartForwardSpec(parts, spec)
case 3:
return parseThreePartForwardSpec(parts)
case 4:
return parseFourPartForwardSpec(parts)
default:
return "", "", fmt.Errorf("invalid port forward specification: %s", spec)
}
}
// parseTwoPartForwardSpec handles "port:unix_socket" format.
func parseTwoPartForwardSpec(parts []string, spec string) (string, string, error) {
if isUnixSocket(parts[1]) {
localAddr := "localhost:" + parts[0]
remoteAddr := parts[1]
return localAddr, remoteAddr, nil
}
return "", "", fmt.Errorf("invalid port forward specification: %s (expected format: [local_host:]local_port:remote_host:remote_port or [local_host:]local_port:unix_socket)", spec)
}
// parseThreePartForwardSpec handles "port:host:hostport" or "host:port:unix_socket" formats.
func parseThreePartForwardSpec(parts []string) (string, string, error) {
if isUnixSocket(parts[2]) {
localHost := normalizeLocalHost(parts[0])
localAddr := localHost + ":" + parts[1]View on GitHub (pinned to 93e97f4bf1)
Solutions
- Reduce to at most 4 parts: [local_host:]local_port:remote_host:remote_port.
- Bracket IPv6 local hosts so the spec is routed to parseIPv6ForwardSpec: -L [::1]:8080:host:80.
- Drop trailing colons and extra segments introduced by copy-paste or templating.
Example fix
# before netbird ssh -L ::1:8080:host:80 peer1 # -> invalid port forward specification: ::1:8080:host:80 # after netbird ssh -L '[::1]:8080:host:80' peer1
Defensive patterns
Strategy: validation
Validate before calling
n := strings.Count(spec, ":") // +1 = parts count; require 2..4 parts
if n < 1 || n > 3 {
if strings.Count(spec, ":") >= 3 && strings.HasPrefix(spec, "[") && !strings.Contains(spec, "]:") {
return fmt.Errorf("IPv6 spec %q must be written as [v6]:port:host:hostport", spec)
}
return fmt.Errorf("spec %q must have 2-4 colon parts", spec)
} Type guard
func isWellFormedForwardSpec(s string) bool {
if strings.HasPrefix(s, "[") {
return strings.Contains(s, "]:")
}
n := strings.Count(s, ":")
return n >= 1 && n <= 3 // 2-4 parts
} Try / catch
if len(parts) > 4 {
// over-segmented: usually an unbracketed IPv6 or a trailing ':' —
// re-normalize (bracket v6, trim separators) and retry once, else reject
} Prevention
- Bracket IPv6 hosts at the moment of construction, not later — most >4-part failures are v6 literals.
- Trim trailing colons from template output (spec.TrimSuffix(":")).
- Keep a regexp lint in CI for forward flags: ^\[?[0-9a-zA-Z.:%/_-]+\]?:... with bounded part count.
- Prefer names or IPv4 for remote targets; the 4-part grammar has no slot for a second v6 literal.
When it happens
Trigger: `-L 8080:host:80:extra` (5 parts), unbracketed IPv6 with ports like `-L ::1:8080:host:80` (6 parts and no leading '['), or specs pasted with a trailing `:`. The '[...]' guard earlier only diverts specs that both start with '[' and contain ']:' — anything else with >4 parts hits this branch.
Common situations: Adding an extra hop or protocol suffix by mistake; unbracketed IPv6 addresses (the classic — every v6 literal adds 7+ colons); appending a trailing separator in generated config.
Related errors
- invalid IPv6 port forward specification: %s
- invalid IPv6 port forward specification: %s (expected [ipv6]
- 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/67a1f23f33ec4fbb.
Report an issue: GitHub.