{"record":{"id":"2ae9c3877b3bf130","repo":"netbirdio/netbird","slug":"invalid-ipv6-port-forward-specification-s-expec","errorCode":null,"errorMessage":"invalid IPv6 port forward specification: %s (expected [ipv6]:port:host:hostport)","messagePattern":"invalid IPv6 port forward specification: (.+?) \\(expected \\[ipv6\\]:port:host:hostport\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/ssh.go","lineNumber":777,"sourceCode":"\tlocalHost := normalizeLocalHost(parts[0])\n\tlocalAddr := localHost + \":\" + parts[1]\n\tremoteAddr := parts[2] + \":\" + parts[3]\n\treturn localAddr, remoteAddr, nil\n}\n\n// parseIPv6ForwardSpec handles \"[host]:port:host:hostport\" format.\nfunc parseIPv6ForwardSpec(spec string) (string, string, error) {\n\tidx := strings.Index(spec, \"]:\")\n\tif idx == -1 {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid IPv6 port forward specification: %s\", spec)\n\t}\n\n\tipv6Host := spec[:idx+1]\n\tremaining := spec[idx+2:]\n\n\tparts := strings.Split(remaining, \":\")\n\tif len(parts) != 3 {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid IPv6 port forward specification: %s (expected [ipv6]:port:host:hostport)\", spec)\n\t}\n\n\tlocalAddr := ipv6Host + \":\" + parts[0]\n\tremoteAddr := parts[1] + \":\" + parts[2]\n\treturn localAddr, remoteAddr, nil\n}\n\n// isUnixSocket checks if a path is a Unix socket path.\nfunc isUnixSocket(path string) bool {\n\treturn strings.HasPrefix(path, \"/\") || strings.HasPrefix(path, \"./\")\n}\n\n// normalizeLocalHost converts \"*\" to \"\" for binding to all interfaces (dual-stack).\nfunc normalizeLocalHost(host string) string {\n\tif host == \"*\" {\n\t\treturn \"\"\n\t}\n\treturn host","sourceCodeStart":759,"sourceCodeEnd":795,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/ssh.go#L759-L795","documentation":"Returned by parseIPv6ForwardSpec when the bracketed IPv6 host was split off successfully (a ']:' was found) but the remainder does not split into exactly three colon-separated parts — local port, remote host, remote port. The expected full grammar is [ipv6]:port:host:hostport, so remainders with 2 or 4+ parts are rejected with this format-hint message.","triggerScenarios":"`-L [::1]:8080:host peer` (remainder `8080:host` — 2 parts, remote port missing), `-L [::1]:8080:host:80:extra peer` (4 parts), or a remainder containing another unbracketed IPv6 literal such as `[::1]:8080:2001:db8::1:80`, which splits into more than three parts and fails.","commonSituations":"Remembering to bracket the local v6 host but not the remote one; omitting the remote port in the IPv6 form specifically; extra segments from templating. A remote IPv6 destination cannot actually be expressed in this grammar — only the local bind side supports brackets.","solutions":["Use exactly three parts after the bracketed host: [v6]:local_port:remote_host:remote_port, with the remote host as a name or IPv4 (e.g., [::1]:8080:10.10.0.5:80).","Give the remote side its explicit numeric port — the IPv6 form has no shorter variant.","If the destination must be IPv6, note the parser's limit and route via a name or an IPv4-mapped intermediate instead of a bracketed remote literal."],"exampleFix":"# before\nnetbird ssh -L '[::1]:8080:host' peer1\n# -> invalid IPv6 port forward specification: 8080:host (expected [ipv6]:port:host:hostport)\n\n# after\nnetbird ssh -L '[::1]:8080:host:80' peer1","handlingStrategy":"validation","validationCode":"// after splitting off [v6]:, the remainder must be exactly port:host:port\nrem := spec[idx+2:]\nparts := strings.Split(rem, \":\")\nif len(parts) != 3 {\n\treturn fmt.Errorf(\"after [v6]: expected port:host:hostport, got %d parts in %q\", len(parts), rem)\n}\nif p, _ := strconv.Atoi(parts[0]); p < 1 || p > 65535 { return fmt.Errorf(\"bad local port\") }\nif p, _ := strconv.Atoi(parts[2]); p < 1 || p > 65535 { return fmt.Errorf(\"bad remote port\") }","typeGuard":"func isCompleteV6Forward(s string) bool {\n\ti := strings.Index(s, \"]:\")\n\tif i == -1 {\n\t\treturn false\n\t}\n\treturn len(strings.Split(s[i+2:], \":\")) == 3\n}","tryCatchPattern":"if len(parts) != 3 {\n\t// remainder over/under-segmented: missing remote port is the common case;\n\t// surface the expected [ipv6]:port:host:hostport shape verbatim\n}","preventionTips":["Memorize the fixed shape: bracketed v6 host plus exactly three more colon parts.","Do not put an IPv6 literal on the remote side — the grammar has no slot for it; use a name or IPv4.","Build v6 forwards from typed fields so part count is guaranteed by construction.","Table-test your spec builder with [::1] fixtures in CI."],"tags":["go","ssh","port-forwarding","ipv6","validation"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}