fatedier/frp · error

parse flags from ssh client error: %v

Error message

parse flags from ssh client error: %v

What it means

Returned by the frp SSH tunnel server when the command string the client sent after connecting (the 'extra payload', e.g. "tcp 127.0.0.1:22") cannot be parsed into frpc proxy flags via the embedded cobra command. The raw parse error is also sent back to the client over the SSH channel before the wrapper error is returned, so the user usually sees the underlying reason on their terminal.

Source

Thrown at pkg/ssh/server.go:111

	if err != nil {
		return err
	}

	s.sshConn = sshConn

	addr, extraPayload, err := s.waitForwardAddrAndExtraPayload(channels, requests, 3*time.Second)
	if err != nil {
		return err
	}

	clientCfg, pc, helpMessage, err := s.parseClientAndProxyConfigurer(addr, extraPayload)
	if err != nil {
		if errors.Is(err, flag.ErrHelp) {
			s.writeToClient(helpMessage)
			return nil
		}
		s.writeToClient(err.Error())
		return fmt.Errorf("parse flags from ssh client error: %v", err)
	}
	if err := clientCfg.Complete(); err != nil {
		s.writeToClient(fmt.Sprintf("failed to complete client config: %v", err))
		return fmt.Errorf("complete client config error: %v", err)
	}
	if sshConn.Permissions != nil {
		clientCfg.User = util.EmptyOr(sshConn.Permissions.Extensions["user"], clientCfg.User)
	}
	pc.Complete()

	vc, err := virtual.NewClient(virtual.ClientOptions{
		Common: clientCfg,
		Spec: &msg.ClientSpec{
			Type: "ssh-tunnel",
			// If ssh does not require authentication, then the virtual client needs to authenticate through a token.
			// Otherwise, once ssh authentication is passed, the virtual client does not need to authenticate again.
			AlwaysAuthPass: !s.sc.NoClientAuth,
		},

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Read the message written back to your SSH session — it contains the exact cobra parse error before this wrapper fires.
  2. Use a supported proxy type as the first token: tcp, http, https, tcpmux, or stcp.
  3. Run `ssh -p port v0@host -- --help` (or an invalid flag) to get the full usage string listing accepted flags.
  4. Quote the whole command as one argument: ssh v0@host "tcp 127.0.0.1:22 --remotePort 6000".

Example fix

# before
ssh -p 7000 v0@frps "udp 127.0.0.1:53"

# after
ssh -p 7000 v0@frps "tcp 127.0.0.1:53 --remotePort 6000"
Defensive patterns

Strategy: validation

Validate before calling

// validate an SSH tunnel command before sending it
var supportedSSHProxyTypes = []string{"tcp", "http", "https", "tcpmux", "stcp"}

func ValidateTunnelCommand(cmd string) error {
    fields := strings.Fields(cmd)
    if len(fields) == 0 {
        return fmt.Errorf("empty command")
    }
    if !slices.Contains(supportedSSHProxyTypes, fields[0]) {
        return fmt.Errorf("unsupported type %q, want one of %v", fields[0], supportedSSHProxyTypes)
    }
    return nil
}

Prevention

When it happens

Trigger: Calling ssh with an unknown proxy type token (e.g. `ssh v0@host "udp ..."`), passing malformed flags (--remotePort not-a-number), unknown flags not registered in SSH mode, or a payload that fails cobra's ParseFlags. Not triggered by flag.ErrHelp, which prints usage and exits cleanly.

Common situations: Users assuming all frpc proxy types work over SSH (only tcp/http/https/tcpmux/stcp do); typos in flag names; copying an frpc.ini stanza into the ssh command line; shell quoting eating the payload so it arrives empty or split incorrectly.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/3449b3f38b5b4690. Report an issue: GitHub.