netbirdio/netbird · error

invalid remote address: %w

Error message

invalid remote address: %w

What it means

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.

Source

Thrown at client/cmd/ssh.go:638

	for _, forward := range remoteForwards {
		if err := parseAndStartRemoteForward(ctx, c, forward, cmd); err != nil {
			return fmt.Errorf("remote port forward %s: %w", forward, err)
		}
	}

	return nil
}

// parseAndStartLocalForward parses and starts a local port forward (-L)
func parseAndStartLocalForward(ctx context.Context, c *sshclient.Client, forward string, cmd *cobra.Command) error {
	localAddr, remoteAddr, err := parsePortForwardSpec(forward)
	if err != nil {
		return err
	}

	if err := validateDestinationPort(remoteAddr); err != nil {
		return fmt.Errorf("invalid remote address: %w", err)
	}

	log.Debugf("Local port forwarding: %s -> %s", localAddr, remoteAddr)

	go func() {
		if err := c.LocalPortForward(ctx, localAddr, remoteAddr); err != nil && !errors.Is(err, context.Canceled) {
			cmd.Printf("Local port forward error: %v\n", err)
		}
	}()

	return nil
}

// parseAndStartRemoteForward parses and starts a remote port forward (-R)
func parseAndStartRemoteForward(ctx context.Context, c *sshclient.Client, forward string, cmd *cobra.Command) error {
	remoteAddr, localAddr, err := parsePortForwardSpec(forward)
	if err != nil {
		return err

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Append a numeric destination port 1-65535: -L 8080:host:80.
  2. 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.
  3. Replace service-name ports with numbers; only strconv.Atoi-passing values are accepted.
  4. Echo the spec before running when it is built from variables, to catch empty segments early.

Example fix

# before
netbird ssh -L 8080:dbhost peer1
# -> start port forwarding: local port forward 8080:dbhost: invalid remote address: parse address dbhost: address dbhost: missing port in address

# after
netbird ssh -L 8080:dbhost:5432 peer1
Defensive patterns

Strategy: validation

Validate before calling

// pre-check the -L destination exactly like the CLI does
func validateRemoteTarget(addr string) error {
	if strings.HasPrefix(addr, "/") || strings.HasPrefix(addr, "./") {
		return nil // unix socket: exempt
	}
	if !strings.Contains(addr, ":") {
		return fmt.Errorf("destination %q needs :port", addr)
	}
	_, portStr, err := net.SplitHostPort(addr)
	if err != nil {
		return err
	}
	p, err := strconv.Atoi(portStr)
	if err != nil || p < 1 || p > 65535 {
		return fmt.Errorf("bad port %q", portStr)
	}
	return nil
}

Try / catch

if err := validateDestinationPort(remoteAddr); err != nil {
	return fmt.Errorf("invalid remote address: %w", err)
	// classify by wrapped err: SplitHostPort -> missing port/IPv6 brackets;
	// Atoi -> non-numeric; sentinel strings -> zero/range
}

Prevention

When it happens

Trigger: `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.

Common situations: 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).

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/b7486d0c3724fd3d. Report an issue: GitHub.