{"record":{"id":"73efca4a29450977","repo":"netbirdio/netbird","slug":"invalid-port-forward-specification-s-expected-f","errorCode":null,"errorMessage":"invalid port forward specification: %s (expected format: [local_host:]local_port:remote_target)","messagePattern":"invalid port forward specification: (.+?) \\(expected format: \\[local_host:\\]local_port:remote_target\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/ssh.go","lineNumber":719,"sourceCode":"}\n\n// parsePortForwardSpec parses port forward specifications like \"8080:localhost:80\" or \"[::1]:8080:localhost:80\".\n// Also supports Unix sockets like \"8080:/tmp/socket\" or \"127.0.0.1:8080:/tmp/socket\".\nfunc parsePortForwardSpec(spec string) (string, string, error) {\n\t// Support formats:\n\t// port:host:hostport  -> localhost:port -> host:hostport\n\t// host:port:host:hostport  -> host:port -> host:hostport\n\t// [host]:port:host:hostport -> [host]:port -> host:hostport\n\t// port:unix_socket_path -> localhost:port -> unix_socket_path\n\t// host:port:unix_socket_path -> host:port -> unix_socket_path\n\n\tif strings.HasPrefix(spec, \"[\") && strings.Contains(spec, \"]:\") {\n\t\treturn parseIPv6ForwardSpec(spec)\n\t}\n\n\tparts := strings.Split(spec, \":\")\n\tif len(parts) < 2 {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid port forward specification: %s (expected format: [local_host:]local_port:remote_target)\", spec)\n\t}\n\n\tswitch len(parts) {\n\tcase 2:\n\t\treturn parseTwoPartForwardSpec(parts, spec)\n\tcase 3:\n\t\treturn parseThreePartForwardSpec(parts)\n\tcase 4:\n\t\treturn parseFourPartForwardSpec(parts)\n\tdefault:\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid port forward specification: %s\", spec)\n\t}\n}\n\n// parseTwoPartForwardSpec handles \"port:unix_socket\" format.\nfunc parseTwoPartForwardSpec(parts []string, spec string) (string, string, error) {\n\tif isUnixSocket(parts[1]) {\n\t\tlocalAddr := \"localhost:\" + parts[0]","sourceCodeStart":701,"sourceCodeEnd":737,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/ssh.go#L701-L737","documentation":"Returned by parsePortForwardSpec when splitting the spec on ':' yields fewer than two parts — i.e., the spec contains no colon at all. The message embeds the expected grammar `[local_host:]local_port:remote_target`. This fires before the 2/3/4-part handlers run, so a single-token spec like `8080` never reaches socket/port validation.","triggerScenarios":"`netbird ssh -L 8080 peer` (a bare port with no colon), `-R 9000` , or a spec whose colon was eaten by shell parsing/quoting so only one field remains. Applies identically to -L and -R since both call parsePortForwardSpec first.","commonSituations":"Assuming the flag takes just a port and the target is implied; YAML/JSON or env-file values losing the colon; HCL/terraform heredocs interpreting `8080:host:80` and emitting only the first token; muscle memory from tools with different flag shapes.","solutions":["Write the spec with at least two colon-separated parts: for -L the minimum full form is local_port:remote_host:remote_port (or port:/unix/path for sockets).","Quote the argument: -L \"8080:localhost:80\" so no shell or config layer can split it.","If the value comes from config, print it once at render time to confirm colons survived."],"exampleFix":"# before\nnetbird ssh -L 8080 peer1\n# -> invalid port forward specification: 8080 (expected format: [local_host:]local_port:remote_target)\n\n# after\nnetbird ssh -L 8080:localhost:80 peer1","handlingStrategy":"validation","validationCode":"if !strings.Contains(spec, \":\") {\n\treturn fmt.Errorf(\"forward spec %q must contain at least one ':' (port:target...)\", spec)\n}","typeGuard":"func hasForwardColon(s string) bool { return strings.Count(s, \":\") >= 1 }","tryCatchPattern":"if len(parts) < 2 {\n\t// spec had no colon at all: treat as a construction bug upstream,\n\t// log the raw spec + its source (env var/config key) for tracing\n}","preventionTips":["Quote forward specs in every layer: -L \"8080:host:80\".","When rendering from templates, assert the rendered string still contains colons before exec.","Store forwards as structured data (host+ports) and serialize only at invocation; never hand-edit strings.","Add a one-line sanity check in wrappers: [[ $spec == *:* ]] || exit with a clear message."],"tags":["go","ssh","port-forwarding","validation","cli"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}