{"record":{"id":"7bde62869893ac92","repo":"netbirdio/netbird","slug":"invalid-port-s-w","errorCode":null,"errorMessage":"invalid port %s: %w","messagePattern":"invalid port (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/ssh.go","lineNumber":689,"sourceCode":"\treturn nil\n}\n\n// validateDestinationPort checks that the destination address has a valid port.\n// Port 0 is only valid for bind addresses (where the OS picks an available port),\n// not for destination addresses where we need to connect.\nfunc validateDestinationPort(addr string) error {\n\tif strings.HasPrefix(addr, \"/\") || strings.HasPrefix(addr, \"./\") {\n\t\treturn nil\n\t}\n\n\t_, portStr, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"parse address %s: %w\", addr, err)\n\t}\n\n\tport, err := strconv.Atoi(portStr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid port %s: %w\", portStr, err)\n\t}\n\n\tif port == 0 {\n\t\treturn fmt.Errorf(\"port 0 is not valid for destination address\")\n\t}\n\n\tif port < 0 || port > 65535 {\n\t\treturn fmt.Errorf(\"port %d out of range (1-65535)\", port)\n\t}\n\n\treturn nil\n}\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","sourceCodeStart":671,"sourceCodeEnd":707,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/ssh.go#L671-L707","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Replace the service name with its numeric port: https -> 443, http -> 80, ssh -> 22.","Strip protocol suffixes and whitespace from scripted inputs (`${PORT%%/*}` and trims).","Echo the final spec when it is variable-built to catch empty port segments before invoking netbird ssh."],"exampleFix":"# before\nnetbird ssh -L 8443:intranet:https peer1\n# -> invalid remote address: invalid port https: strconv.Atoi: parsing \"https\": invalid syntax\n\n# after\nnetbird ssh -L 8443:intranet:443 peer1","handlingStrategy":"validation","validationCode":"// accept only numeric ports, resolve names once at config load\nsvcPorts := map[string]int{\"http\": 80, \"https\": 443, \"ssh\": 22, \"postgres\": 5432}\nfunc normalizePort(p string) (int, error) {\n\tp = strings.TrimSpace(strings.TrimSuffix(strings.TrimSuffix(p, \"/tcp\"), \"/udp\"))\n\tif n, ok := svcPorts[p]; ok {\n\t\treturn n, nil\n\t}\n\tn, err := strconv.Atoi(p)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"port %q must be numeric\", p)\n\t}\n\treturn n, nil\n}","typeGuard":"func isNumericPort(s string) bool {\n\tn, err := strconv.Atoi(s)\n\treturn err == nil && n >= 1 && n <= 65535\n}","tryCatchPattern":"if _, err := strconv.Atoi(portStr); err != nil {\n\t// *strconv.NumError: map service names via getservent/your table,\n\t// or reject with a message that names the offending segment\n}","preventionTips":["Ban service names in generated configs; store ports as integers and stringify only at the CLI boundary.","Strip '/tcp', '/udp' suffixes and whitespace when ingesting port fields from YAML/env.","Echo the composed spec in debug output so a mangled port is visible before the dial.","Validate with strconv.Atoi (not regexes) so your check matches the CLI's exact acceptance."],"tags":["go","ssh","validation","port","cli"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}