fatedier/frp · error
complete client config error: %v
Error message
complete client config error: %v
What it means
Returned when v1.ClientCommonConfig.Complete() fails after the SSH payload flags were successfully parsed. Complete() fills defaults and validates the assembled client config (server address, user, TLS settings, etc.). The underlying reason is both written to the client's SSH session and wrapped in this error on the server side.
Source
Thrown at pkg/ssh/server.go:115
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,
},
HandleWorkConnCb: func(base *v1.ProxyBaseConfig, workConn net.Conn, m *msg.StartWorkConn) bool {
// join workConn and ssh channel
c, err := s.openConn(addr)
if err != nil {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Check the message echoed to the SSH session — it prefixes 'failed to complete client config: <reason>'.
- Strip common-config flags from the ssh command; the SSH tunnel derives server/user/token from the session itself.
- Keep only proxy-level flags (local address/port, remotePort, custom domains) in the SSH payload.
- If you need full common config, use the frpc binary with a config file instead of the SSH tunnel path.
- Verify frpc-side tooling/frps versions match when driving this programmatically.
Example fix
# before ssh v0@frps "tcp 127.0.0.1:22 --serverAddr frps.example.com --token xxx" # after (common config comes from the session; keep proxy flags only) ssh v0@frps "tcp 127.0.0.1:22 --remotePort 6000"
Defensive patterns
Strategy: validation
Try / catch
ssh v0@host "tcp 127.0.0.1:22 --remotePort 6000" 2>&1 | tee /dev/stderr | grep -q "failed to complete client config" && echo "fix flags: use proxy-level flags only"
Prevention
- Keep common-config settings out of the ssh payload; they are derived from the session.
- Use the frpc binary with a config file when you need full common-config control.
- Match frps and frpc/ssh-tunnel tooling versions.
When it happens
Trigger: Flags parsed but the resulting common config is inconsistent — e.g. server address derived from the connection is unusable, a transport option combination is rejected by Complete(), or an auth field is invalid. The concrete sub-error from Complete() is included after 'complete client config error: '.
Common situations: Passing common-config flags over SSH that conflict with the fixed server-side values (token, user, tls settings); version drift where an old ssh client sends flags that the new frps Complete() rejects; specifying options that only make sense in standalone frpc mode.
Related errors
- internal error
- invalid proxy type: %s, support types: %v
- envelope.msg
- ErrInvalidArgument
- ErrHealthCheckType
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/e9171e11d3e3b6ea.
Report an issue: GitHub.