{"record":{"id":"b7486d0c3724fd3d","repo":"netbirdio/netbird","slug":"invalid-remote-address-w","errorCode":null,"errorMessage":"invalid remote address: %w","messagePattern":"invalid remote address: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/ssh.go","lineNumber":638,"sourceCode":"\n\tfor _, forward := range remoteForwards {\n\t\tif err := parseAndStartRemoteForward(ctx, c, forward, cmd); err != nil {\n\t\t\treturn fmt.Errorf(\"remote port forward %s: %w\", forward, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// parseAndStartLocalForward parses and starts a local port forward (-L)\nfunc parseAndStartLocalForward(ctx context.Context, c *sshclient.Client, forward string, cmd *cobra.Command) error {\n\tlocalAddr, remoteAddr, err := parsePortForwardSpec(forward)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := validateDestinationPort(remoteAddr); err != nil {\n\t\treturn fmt.Errorf(\"invalid remote address: %w\", err)\n\t}\n\n\tlog.Debugf(\"Local port forwarding: %s -> %s\", localAddr, remoteAddr)\n\n\tgo func() {\n\t\tif err := c.LocalPortForward(ctx, localAddr, remoteAddr); err != nil && !errors.Is(err, context.Canceled) {\n\t\t\tcmd.Printf(\"Local port forward error: %v\\n\", err)\n\t\t}\n\t}()\n\n\treturn nil\n}\n\n// parseAndStartRemoteForward parses and starts a remote port forward (-R)\nfunc parseAndStartRemoteForward(ctx context.Context, c *sshclient.Client, forward string, cmd *cobra.Command) error {\n\tremoteAddr, localAddr, err := parsePortForwardSpec(forward)\n\tif err != nil {\n\t\treturn err","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/ssh.go#L620-L656","documentation":"Wraps validateDestinationPort(remoteAddr) in the -L (local forward) path: after parsePortForwardSpec splits the spec, the remote target must carry a usable port unless it is a unix socket path (leading / or ./). The wrapped error is one of the validateDestinationPort failures — SplitHostPort parse error, non-numeric port, port 0, or out-of-range port.","triggerScenarios":"`netbird ssh -L 8080:host peer` where parse accepted 3 parts but the validator rejects `host` (no port); `-L 8080:host:0`; `-L 8080:host:70000`; `-L 8080:[v6]:x` style targets that fail SplitHostPort; any remote target that is neither an absolute/relative socket path nor host:validport.","commonSituations":"Cutting a forward spec down to host only; using service names instead of numeric ports (`:http` is not supported — only digits); empty port from an unset shell variable (`-L 8080:host:$PORT` with PORT empty yields parse or port-0 errors).","solutions":["Append a numeric destination port 1-65535: -L 8080:host:80.","If the target really is a unix socket, write it as an absolute path (-L 8080:/var/run/svc.sock) or ./relative so the validator skips port checks.","Replace service-name ports with numbers; only strconv.Atoi-passing values are accepted.","Echo the spec before running when it is built from variables, to catch empty segments early."],"exampleFix":"# before\nnetbird ssh -L 8080:dbhost peer1\n# -> start port forwarding: local port forward 8080:dbhost: invalid remote address: parse address dbhost: address dbhost: missing port in address\n\n# after\nnetbird ssh -L 8080:dbhost:5432 peer1","handlingStrategy":"validation","validationCode":"// pre-check the -L destination exactly like the CLI does\nfunc validateRemoteTarget(addr string) error {\n\tif strings.HasPrefix(addr, \"/\") || strings.HasPrefix(addr, \"./\") {\n\t\treturn nil // unix socket: exempt\n\t}\n\tif !strings.Contains(addr, \":\") {\n\t\treturn fmt.Errorf(\"destination %q needs :port\", addr)\n\t}\n\t_, portStr, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp, err := strconv.Atoi(portStr)\n\tif err != nil || p < 1 || p > 65535 {\n\t\treturn fmt.Errorf(\"bad port %q\", portStr)\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if err := validateDestinationPort(remoteAddr); err != nil {\n\treturn fmt.Errorf(\"invalid remote address: %w\", err)\n\t// classify by wrapped err: SplitHostPort -> missing port/IPv6 brackets;\n\t// Atoi -> non-numeric; sentinel strings -> zero/range\n}","preventionTips":["Treat 'destination without port' as a build error in spec generators — assert host:port shape.","Map service names to numbers once, in one lookup table, at config-load time.","Bracket-check IPv6 literals before composing specs.","Add a smoke test that runs netbird ssh -L with a fixture list of specs to catch regressions in your templates."],"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"}